Compare commits

..

2 Commits

Author SHA1 Message Date
胡尧
6c8677e9e6 修改服务地址 2025-06-05 09:40:18 +08:00
胡尧
e8a968c5a7 修改工单获取前置三元检测方法 2025-05-15 08:45:55 +08:00
55 changed files with 769 additions and 1744 deletions

View File

@@ -8,19 +8,18 @@
'category': 'purchase', 'category': 'purchase',
'depends': ['sf_manufacturing', 'purchase_request'], 'depends': ['sf_manufacturing', 'purchase_request'],
'data': [ 'data': [
'security/ir.model.access.csv',
'views/sale_order_view.xml', 'views/sale_order_view.xml',
'views/mrp_production.xml', 'views/mrp_production.xml',
'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',
'wizard/purchase_request_wizard_views.xml', 'views/stock_picking_views.xml',
], ],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [
'jikimo_purchase_request/static/src/**/*' 'jikimo_purchase_request/static/src/**/*'
], ],
}, },
'application': True, 'application': True,
'installable': True, 'installable': True,
'auto_install': False, 'auto_install': False,

View File

@@ -410,7 +410,7 @@ msgstr "显示名称"
#: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_form #: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_form
#: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_search #: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_search
msgid "Done" msgid "Done"
msgstr "关闭" msgstr "完成"
#. module: purchase_request #. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__move_dest_ids #: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__move_dest_ids

View File

@@ -13,7 +13,11 @@ class ProductTemplate(models.Model):
template_id.purchase_request = product_id.purchase_request template_id.purchase_request = product_id.purchase_request
return template_id return template_id
class ProdcutProduct(models.Model):
_inherit = 'product.product'
def copy_template(self, product_template_id): def copy_template(self, product_template_id):
""" 复制成品模板时,复制采购申请 """ """ 复制成品模板时,复制采购申请 """
super(ProductTemplate, self).copy_template(product_template_id) super(ProdcutProduct, self).copy_template(product_template_id)
self.purchase_request = product_template_id.purchase_request self.purchase_request = product_template_id.purchase_request

View File

@@ -1,7 +1,6 @@
import re import re
import ast import ast
from odoo import models, fields, api, _ from odoo import models, fields, api
from itertools import groupby
class PurchaseRequest(models.Model): class PurchaseRequest(models.Model):
@@ -30,54 +29,6 @@ class PurchaseRequest(models.Model):
action['context'] = origin_context action['context'] = origin_context
return action return action
def button_done(self):
product_qty_map = {key: sum(line.product_qty for line in group) for key, group in
groupby(self.line_ids, key=lambda x: x.product_id.id)}
lines = self.mapped("line_ids.purchase_lines.order_id")
# 采购单产品和数量
product_summary = {}
if lines:
for line in lines:
for line_item in line.order_line:
product_id = line_item.product_id.id
qty = line_item.product_qty
if product_id in product_summary:
product_summary[product_id] += qty
else:
product_summary[product_id] = qty
# 校验产品数量
discrepancies = []
for product_id, qty in product_qty_map.items():
if product_id in product_summary:
if product_summary[product_id] != qty:
discrepancies.append((product_id, qty, product_summary[product_id]))
else:
discrepancies.append((product_id, qty, 0))
if discrepancies:
# 弹出提示框
message = "产品数量不一致:\n"
for product_id, required_qty, order_qty in discrepancies:
product_name = self.env['product.product'].browse(product_id).display_name # 获取产品名称
message += f"产品 {product_name},需求数量 {required_qty},关联采购订单数量 {order_qty}(含询价状态)\n"
# 添加确认框
message += "确认关闭?"
return {
'name': _('采购申请'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'jikimo_purchase_request.purchase_request_wizard_wizard_form_view').id,
'form')],
'res_model': 'purchase.request.wizard',
'target': 'new',
'context': {
'default_purchase_request_id': self.id,
'default_message': message,
}}
return super(PurchaseRequest, self).button_done()
class PurchaseRequestLine(models.Model): class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line' _inherit = 'purchase.request.line'
_description = '采购申请明细' _description = '采购申请明细'
@@ -141,7 +92,7 @@ class PurchaseRequestLine(models.Model):
continue continue
if record.product_id.categ_id.name == '坯料': if record.product_id.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', record.product_id.name) match = re.search(r'(S\d{5}-\d)', record.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)

View File

@@ -33,13 +33,3 @@ class StockPicking(models.Model):
'view_mode': 'tree,form', 'view_mode': 'tree,form',
}) })
return action return action
def _action_done(self):
res = super(StockPicking, self)._action_done()
# 将采购申请明细行的move_dest_ids设置为backorder_ids
backorder_ids = self.backorder_ids
if backorder_ids:
purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines
if purchase_request_lines:
purchase_request_lines.move_dest_ids = [(4, x.id) for x in backorder_ids.move_ids]
return res

View File

@@ -1,2 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_purchase_request_wizard_group_user,purchase.request.wizard,model_purchase_request_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_purchase_request_wizard_group_user purchase.request.wizard model_purchase_request_wizard base.group_user 1 1 1 1

View File

@@ -15,12 +15,6 @@
<field name="part_number"/> <field name="part_number"/>
<field name="part_name"/> <field name="part_name"/>
</xpath> </xpath>
<xpath expr="//button[@name='button_done']" position="attributes">
<attribute name="class"/>
</xpath>
<xpath expr="//button[@name='button_in_progress']/following-sibling::button[1]" position="attributes">
<attribute name="class">oe_highlight</attribute>
</xpath>
</field> </field>
</record> </record>

View File

@@ -1,4 +1,3 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
from . import purchase_request_line_make_purchase_order from . import purchase_request_line_make_purchase_order
from . import purchase_request_wizard

View File

@@ -1,12 +0,0 @@
from odoo import models, fields, api
class PurchaseRequestWizard(models.TransientModel):
_name = 'purchase.request.wizard'
_description = '采购申请向导'
purchase_request_id = fields.Many2one('purchase.request', string='采购申请')
message = fields.Char(string='提示', readonly=True)
def confirm(self):
return self.purchase_request_id.write({"state": "done"})

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="purchase_request_wizard_wizard_form_view">
<field name="name">purchase.request.wizard.form.view</field>
<field name="model">purchase.request.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<div>
<div style="white-space: pre-wrap;">
<field name="message"/>
</div>
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
</odoo>

View File

@@ -6,6 +6,10 @@ class ProductTemplate(models.Model):
is_manual_processing = fields.Boolean(string='人工线下加工') is_manual_processing = fields.Boolean(string='人工线下加工')
is_customer_provided = fields.Boolean(string='客供料') is_customer_provided = fields.Boolean(string='客供料')
class ProductProduct(models.Model):
_inherit = 'product.product'
def copy_template(self, product_template_id): def copy_template(self, product_template_id):
if not isinstance(product_template_id, ProductTemplate): if not isinstance(product_template_id, ProductTemplate):
raise ValueError('%s必须是ProductTemplate类型' % product_template_id) raise ValueError('%s必须是ProductTemplate类型' % product_template_id)

View File

@@ -133,7 +133,6 @@ class QualityCheck(models.Model):
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=False, store=True) part_name = fields.Char('零件名称', related='product_id.part_name', readonly=False, store=True)
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=False, store=True) part_number = fields.Char('零件图号', related='product_id.part_number', readonly=False, store=True)
material_name = fields.Char('材料名称', compute='_compute_material_name') material_name = fields.Char('材料名称', compute='_compute_material_name')
model_id = fields.Char('模型ID', related='product_id.model_id')
# # 总数量值为调拨单_产品明细_数量 # # 总数量值为调拨单_产品明细_数量
# total_qty = fields.Float('总数量', compute='_compute_total_qty', readonly=True) # total_qty = fields.Float('总数量', compute='_compute_total_qty', readonly=True)

View File

@@ -493,9 +493,6 @@
<field name="picking_id"/> <field name="picking_id"/>
<field name="lot_id"/> <field name="lot_id"/>
<field name="team_id"/> <field name="team_id"/>
<field name="part_number"/>
<field name="part_name"/>
<field name="model_id"/>
<filter string="In Progress" name="progress" domain="[('quality_state', '=', 'none')]"/> <filter string="In Progress" name="progress" domain="[('quality_state', '=', 'none')]"/>
<filter string="Passed" name="passed" domain="[('quality_state', '=', 'pass')]"/> <filter string="Passed" name="passed" domain="[('quality_state', '=', 'pass')]"/>
<filter string="Failed" name="failed" domain="[('quality_state', '=', 'fail')]"/> <filter string="Failed" name="failed" domain="[('quality_state', '=', 'fail')]"/>

View File

@@ -195,18 +195,10 @@ class PrintingUtils(models.AbstractModel):
text_y = margin + 20 # 文字位置靠近底部 text_y = margin + 20 # 文字位置靠近底部
c.drawString(text_x, text_y, text) c.drawString(text_x, text_y, text)
# 设置字体
if font_found:
c.setFont('SimSun', 12) # 增大字体大小到14pt
else:
# 如果没有找到中文字体,使用默认字体
c.setFont('Helvetica', 120)
logging.warning("未找到中文字体,将使用默认字体")
if buttom_text: if buttom_text:
# 在下方中间添加文字 # 在下方中间添加文字
text = buttom_text text = button_text
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 12) # 准确计算文字宽度 text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度
text_x = (page_width - text_width) / 2 # 文字居中对齐 text_x = (page_width - text_width) / 2 # 文字居中对齐
text_y = margin + 20 # 文字位置靠近底部 text_y = margin + 20 # 文字位置靠近底部
c.drawString(text_x, text_y, text) c.drawString(text_x, text_y, text)

View File

@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models
from . import wizard

View File

@@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': '机企猫智能工厂 需求计划',
'version': '1.0',
'summary': '智能工厂计划管理',
'sequence': 1,
'description': """
在本模块,支持齐套检查与下达生产
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sf_plan', 'jikimo_printing'],
'data': [
'security/ir.model.access.csv',
'views/demand_plan.xml',
'wizard/sf_demand_plan_print_wizard_view.xml',
],
'demo': [
],
'assets': {
'web.assets_qweb': [
],
'web.assets_backend': [
'sf_demand_plan/static/src/scss/style.css',
]
},
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
}

View File

@@ -1 +0,0 @@
from . import controllers

View File

@@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
import logging
import json
from odoo import http, fields, models
from odoo.http import request
from odoo.addons.sf_base.controllers.controllers import MultiInheritController
class SfPlanMrsConnect(http.Controller, MultiInheritController):
@http.route('/api/demand_plan/update_processing_time', type='json', auth='sf_token', methods=['GET', 'POST'],
csrf=False,
cors="*")
def update_processing_time(self, **kw):
"""
根据模型id修改程序工时
:param kw:
:return:
"""
try:
res = {'status': 1, 'message': '成功'}
datas = request.httprequest.data
ret = json.loads(datas)
ret = json.loads(ret['result'])
logging.info('根据模型id修改程序工时:%s' % ret)
demand_plan = request.env['sf.production.demand.plan'].sudo().search(
[('model_id', '=', ret['model_id'])])
if demand_plan:
demand_plan.write(
{'processing_time': ret['total_estimated_time']})
else:
res = {'status': 0, 'message': '未查到该需求计划'}
except Exception as e:
logging.info('update_demand_paln error:%s' % e)
res['status'] = -1
res['message'] = '系统解析错误!'
return json.JSONEncoder().encode(res)

View File

@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from . import sf_production_demand_plan
from . import sale_order

View File

@@ -1,23 +0,0 @@
from odoo import models, fields, api, _
class ReSaleOrder(models.Model):
_inherit = 'sale.order'
def sale_order_create_line(self, product, item):
ret = super(ReSaleOrder, self).sale_order_create_line(product, item)
vals = {
'sale_order_id': ret.order_id.id,
'sale_order_line_id': ret.id,
}
demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
if demand_plan.product_id.machining_drawings_name:
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
wizard_vals = {
'demand_plan_id': demand_plan.id,
'model_id': demand_plan.model_id,
'filename_url': filename_url,
'type': '1',
}
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
return ret

View File

@@ -1,558 +0,0 @@
# -*- coding: utf-8 -*-
import ast
import json
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from odoo.tools import float_compare
from datetime import datetime, timedelta
from odoo.exceptions import UserError
class SfProductionDemandPlan(models.Model):
_name = 'sf.production.demand.plan'
_description = 'sf_production_demand_plan'
def _get_machining_precision(self):
machinings = self.env['sf.machining.accuracy'].sudo().search([])
list = [(m.sync_id, m.name) for m in machinings]
return list
priority = fields.Selection([
('1', '紧急'),
('2', ''),
('3', ''),
('4', ''),
], string='优先级', default='3')
status = fields.Selection([
('10', '草稿'),
('20', '待确认'),
('30', '需求确认'),
('50', '待下达生产'),
('60', '已下达'),
('100', '取消'),
], string='状态', compute='_compute_status', store=True)
sale_order_id = fields.Many2one(comodel_name="sale.order",
string="销售订单", readonly=True)
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line",
string="销售订单行", readonly=True)
company_id = fields.Many2one(
related='sale_order_id.company_id',
store=True, index=True, precompute=True)
partner_id = fields.Many2one(
comodel_name='res.partner',
related='sale_order_line_id.order_partner_id',
string="客户",
store=True, index=True)
order_remark = fields.Text(related='sale_order_id.remark',
string="订单备注", store=True)
glb_url = fields.Char(related='sale_order_line_id.glb_url', string='glb文件地址')
product_id = fields.Many2one(
comodel_name='product.product',
related='sale_order_line_id.product_id',
string='产品', store=True, index=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
part_name = fields.Char('零件名称', related='product_id.part_name')
part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True)
supply_method = fields.Selection([
('automation', "自动化产线加工"),
('manual', "人工线下加工"),
('purchase', "外购"),
('outsourcing', "委外加工"),
], string='供货方式', related='sale_order_line_id.supply_method', store=True)
product_uom_qty = fields.Float(
string="需求数量",
related='sale_order_line_id.product_uom_qty', store=True)
deadline_of_delivery = fields.Date('客户交期', related='sale_order_id.deadline_of_delivery', store=True)
inventory_quantity_auto_apply = fields.Float(
string="成品库存",
compute='_compute_inventory_quantity_auto_apply'
)
qty_delivered = fields.Float(
"交货数量", related='sale_order_line_id.qty_delivered')
qty_to_deliver = fields.Float(
"待交货数量", related='sale_order_line_id.qty_to_deliver')
model_long = fields.Char('尺寸', compute='_compute_model_long')
materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度',
related='product_id.model_machining_precision')
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
'plan_process_parameter_rel',
string='表面工艺',
compute='_compute_model_process_parameters_ids'
, store=True
)
product_remark = fields.Char("产品备注", related='product_id.model_remark')
order_code = fields.Char('E-SHOP订单号', related='sale_order_id.order_code')
order_state = fields.Selection(
string='订单状态',
related='sale_order_line_id.state')
route_id = fields.Many2one('stock.route', string='路线', related='sale_order_line_id.route_id', store=True)
contract_date = fields.Date('合同日期')
date_order = fields.Datetime('下单日期', related='sale_order_id.date_order')
contract_code = fields.Char('合同号')
plan_remark = fields.Text("计划备注")
material_check = fields.Selection([
('0', "未齐套"),
('1', "已齐套"),
], string='投料齐套检查', compute='_compute_material_check', store=True)
processing_time = fields.Char('程序工时', readonly=True)
planned_start_date = fields.Date('计划开工日期')
actual_start_date = fields.Date('实际开工日期', compute='_compute_actual_start_date', store=True)
actual_end_date = fields.Date('实际完工日期', compute='_compute_actual_end_date', store=True)
print_count = fields.Char('打印次数', default='T0C0', readonly=True)
sequence = fields.Integer('序号')
hide_action_open_mrp_production = fields.Boolean(
string='显示待工艺确认按钮',
compute='_compute_hid_button',
default=False
)
hide_action_purchase_orders = fields.Boolean(
string='显示采购按钮',
compute='_compute_hide_action_purchase_orders',
default=False
)
hide_action_stock_picking = fields.Boolean(
string='显示调拨单按钮',
compute='_compute_hide_action_stock_picking',
default=False
)
hide_action_outsourcing_stock_picking = fields.Boolean(
string='委外显示调拨单按钮',
compute='_compute_hide_action_stock_picking',
default=False
)
hide_action_view_programming = fields.Boolean(
string='显示编程单按钮',
compute='_compute_hid_button',
default=False
)
outsourcing_purchase_request = fields.Char('委外采购申请单')
@api.depends('sale_order_id.state', 'sale_order_id.mrp_production_ids.schedule_state', 'sale_order_id.order_line',
'sale_order_id.mrp_production_ids.state')
def _compute_status(self):
for record in self:
if record.sale_order_id:
sale_order_state = record.sale_order_id.state
if sale_order_state in ('draft', 'sent', 'supply method'):
record.status = '20' # 待确认
if record.supply_method in ('purchase', 'outsourcing') and sale_order_state in (
'sale', 'processing', 'physical_distribution', 'delivered',
'done') and sale_order_state != 'cancel':
record.status = '60' # 已下达
if record.supply_method in ('automation', 'manual'):
if sale_order_state in (
'sale', 'processing', 'physical_distribution', 'delivered',
'done') and sale_order_state != 'cancel':
record.status = '30' # 需求确认
# 检查所有制造订单的排程单状态,有一个为待排程状态,就为待下达生产
pending_productions = record.sale_order_id.mrp_production_ids.filtered(
lambda p: p.state == 'confirmed' and p.product_id.id == record.product_id.id
)
if pending_productions:
record.status = '50' # 待下达生产
# 检查所有制造订单的排程单状态
if record.sale_order_id.mrp_production_ids and all(
order.product_id == record.product_id and order.schedule_state != '未排' for order in
record.sale_order_id.mrp_production_ids):
record.status = '60' # 已下达
if sale_order_state == 'cancel' or not record.sale_order_line_id:
record.status = '100' # 取消
@api.depends('product_id.part_number', 'product_id.model_name')
def _compute_part_number(self):
for line in self:
if line.product_id:
if line.product_id.part_number:
line.part_number = line.product_id.part_number
else:
if line.product_id.model_name:
line.part_number = line.product_id.model_name.rsplit('.', 1)[0]
else:
line.part_number = None
@api.depends('product_id.length', 'product_id.width', 'product_id.height')
def _compute_model_long(self):
for line in self:
if line.product_id:
line.model_long = f"{line.product_id.length}*{line.product_id.width}*{line.product_id.height}"
else:
line.model_long = None
@api.depends('product_id.materials_id')
def _compute_materials_id(self):
for line in self:
if line.product_id:
line.materials_id = f"{line.product_id.materials_id.name}*{line.product_id.materials_type_id.name}"
else:
line.materials_id = None
@api.depends('product_id.model_process_parameters_ids')
def _compute_model_process_parameters_ids(self):
for line in self:
if line.product_id and line.product_id.model_process_parameters_ids:
line.model_process_parameters_ids = [(6, 0, line.product_id.model_process_parameters_ids.ids)]
else:
line.model_process_parameters_ids = [(5, 0, 0)]
def _compute_inventory_quantity_auto_apply(self):
location_id = self.env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id
product_ids = self.mapped('product_id').ids
if product_ids:
quant_data = self.env['stock.quant'].read_group(
domain=[
('product_id', 'in', product_ids),
('location_id', '=', location_id)
],
fields=['product_id', 'inventory_quantity_auto_apply'],
groupby=['product_id']
)
quantity_map = {item['product_id'][0]: item['inventory_quantity_auto_apply'] for item in quant_data}
else:
quantity_map = {}
for line in self:
if line.product_id:
line.inventory_quantity_auto_apply = quantity_map.get(line.product_id.id, 0.0)
else:
line.inventory_quantity_auto_apply = 0.0
@api.depends('sale_order_id.mrp_production_ids.workorder_ids.date_start')
def _compute_actual_start_date(self):
for record in self:
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
lambda mo: mo.product_id == record.product_id)
if manufacturing_orders:
start_dates = [
workorder.date_start.date() for mo in manufacturing_orders
for workorder in mo.workorder_ids if workorder.date_start
]
record.actual_start_date = min(start_dates) if start_dates else None
else:
record.actual_start_date = None
else:
record.actual_start_date = None
@api.depends('sale_order_id.mrp_production_ids.workorder_ids.state',
'sale_order_id.mrp_production_ids.workorder_ids.date_finished')
def _compute_actual_end_date(self):
for record in self:
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
lambda mo: mo.product_id == record.product_id)
finished_orders = manufacturing_orders.filtered(lambda mo: mo.state == 'done')
if len(finished_orders) >= record.product_uom_qty:
end_dates = [
workorder.date_finished.date() for mo in finished_orders
for workorder in mo.workorder_ids if workorder.date_finished
]
record.actual_end_date = max(end_dates) if end_dates else None
else:
record.actual_end_date = None
else:
record.actual_end_date = None
@api.depends('sale_order_id.mrp_production_ids.move_raw_ids.forecast_availability',
'sale_order_id.mrp_production_ids.move_raw_ids.quantity_done')
def _compute_material_check(self):
for record in self:
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
lambda mo: mo.product_id == record.product_id)
if manufacturing_orders and manufacturing_orders.move_raw_ids:
total_forecast_availability = sum(manufacturing_orders.mapped('move_raw_ids.forecast_availability'))
total_quantity_done = sum(manufacturing_orders.mapped('move_raw_ids.quantity_done'))
total_sum = total_forecast_availability + total_quantity_done
if float_compare(total_sum, record.product_uom_qty,
precision_rounding=record.product_id.uom_id.rounding) >= 0:
record.material_check = '1' # 已齐套
else:
record.material_check = '0' # 未齐套
else:
record.material_check = None
else:
record.material_check = None
@api.constrains('planned_start_date')
def _check_planned_start_date(self):
for record in self:
if record.planned_start_date and record.planned_start_date < fields.Date.today():
raise ValidationError("计划开工日期必须大于或等于今天。")
def release_production_order(self):
pro_plan_list = self.env['sf.production.plan'].search(
[('product_id', '=', self.product_id.id), ('state', '=', 'draft')])
sf_production_line = self.env['sf.production.line'].sudo().search(
[('name', '=', '1#CNC自动生产线')], limit=1)
current_datetime = datetime.now() + timedelta(minutes=3)
current_hour = current_datetime.hour + current_datetime.minute / 60
date_planned_start = None
production_lines = sf_production_line.mrp_workcenter_ids.filtered(lambda b: "自动生产线" in b.name)
if production_lines:
if not production_lines.deal_with_workcenter_calendar(current_datetime):
attendance_list = production_lines.resource_calendar_id.attendance_ids
# 获取所有工作日规则并按星期几分组
attendance_by_day = {}
for attendance in attendance_list:
if attendance.dayofweek not in attendance_by_day:
attendance_by_day[attendance.dayofweek] = []
attendance_by_day[attendance.dayofweek].append(attendance)
for day_offset in range(0, 8):
check_date = current_datetime + timedelta(days=day_offset)
# 日期为星期几
check_day = production_lines.get_current_day_of_week(check_date)
if check_day in attendance_by_day:
day_attendances = attendance_by_day[check_day]
if day_offset == 0:
for attendance in day_attendances:
if current_hour < attendance.hour_to:
# 找到下一个有效时间段
if current_hour < attendance.hour_from:
# 使用开始时间
date_planned_start = check_date.replace(
hour=int(attendance.hour_from),
minute=int((attendance.hour_from % 1) * 60),
second=0,
microsecond=0
)
else:
continue
break
else:
# 不是今天,使用第一个工作时间段
attendance = day_attendances[0]
date_planned_start = check_date.replace(
hour=int(attendance.hour_from),
minute=int((attendance.hour_from % 1) * 60),
second=0,
microsecond=0
)
if date_planned_start:
break
else:
date_planned_start = current_datetime
if date_planned_start:
pro_plan_list.production_line_id = sf_production_line.id
pro_plan_list.date_planned_start = date_planned_start
for pro_plan in pro_plan_list:
pro_plan.do_production_schedule()
def button_action_print(self):
return {
'res_model': 'sf.demand.plan.print.wizard',
'type': 'ir.actions.act_window',
'name': _("打印"),
'domain': [('demand_plan_id', 'in', self.ids)],
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
'target': 'new',
}
@api.depends('sale_order_id.mrp_production_ids.state', 'sale_order_id.mrp_production_ids.programming_state')
def _compute_hid_button(self):
for record in self:
mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == record.product_id.id
)
record.hide_action_open_mrp_production = bool(mrp_production_ids) and record.supply_method in (
'automation', 'manual')
programming_mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
lambda p: p.programming_state == '编程中' and p.product_id.id == record.product_id.id
)
record.hide_action_view_programming = bool(programming_mrp_production_ids)
def _compute_hide_action_purchase_orders(self):
for record in self:
record.hide_action_purchase_orders = False
outsourcing_purchase_request = []
if record.supply_method in ('automation',
'manual') and record.material_check == '0' and not record.sale_order_line_id.is_incoming_material:
mrp_production = record.sale_order_id.mrp_production_ids.filtered(
lambda p: p.product_id.id == record.product_id.id
).sorted(key=lambda p: p.id)
if mrp_production:
raw_materials = mrp_production.mapped('move_raw_ids.product_id')
if raw_materials:
purchase_orders = self.env['purchase.order'].sudo().search([
('state', '=', 'purchase'),
('order_line.product_id', 'in', raw_materials.ids)
])
total_purchase_quantity = sum(
sum(
order.order_line.filtered(
lambda line: line.product_id in raw_materials
).mapped('product_qty')
)
for order in purchase_orders
)
if total_purchase_quantity < record.product_uom_qty:
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', mrp_production[0].name), ('state', '!=', 'done')])
outsourcing_purchase_request.extend(pr_ids.ids)
elif record.supply_method in ('purchase', 'outsourcing'):
pr_ids = None
purchase_orders = self.env['purchase.order'].sudo().search([
('state', 'in', ('purchase', 'done')),
('order_line.product_id', '=', record.product_id.id)
])
total_purchase_quantity = sum(
sum(
order.order_line.filtered(
lambda line: line.product_id in record.product_id
).mapped('product_qty')
)
for order in purchase_orders
)
if total_purchase_quantity < record.product_uom_qty:
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', record.sale_order_id.name), ('state', '!=', 'done')])
outsourcing_purchase_request.extend(pr_ids.ids)
if record.supply_method == 'outsourcing' and not record.sale_order_line_id.is_incoming_material:
bom_line_ids = record.product_id.bom_ids.bom_line_ids
# BOM_数量
total_product_qty = sum(line.product_qty for line in bom_line_ids)
bom_product_ids = bom_line_ids.mapped('product_id')
product_purchase_orders = self.env['purchase.order'].sudo().search([
('state', 'in', ('purchase', 'done')),
('order_line.product_id', 'in', bom_product_ids.ids)
])
# 购订单_数量
total_outsourcing_purchase_quantity = sum(
sum(
order.order_line.filtered(
lambda line: line.product_id in bom_product_ids
).mapped('product_qty')
)
for order in product_purchase_orders
)
if total_outsourcing_purchase_quantity / total_product_qty < record.product_uom_qty:
purchase_request = self.env['purchase.request'].sudo().search(
[('line_ids.product_id', 'in', bom_product_ids.ids),
('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')])
outsourcing_purchase_request.extend(purchase_request.ids)
record.outsourcing_purchase_request = json.dumps(outsourcing_purchase_request)
if outsourcing_purchase_request:
record.hide_action_purchase_orders = True
@api.depends('sale_order_id.mrp_production_ids.picking_ids.state', 'sale_order_id.picking_ids.state')
def _compute_hide_action_stock_picking(self):
for record in self:
record.hide_action_stock_picking = False
record.hide_action_outsourcing_stock_picking = False
if record.supply_method in ('automation', 'manual'):
manufacturing_orders = record.sale_order_id.mrp_production_ids
record.hide_action_stock_picking = bool(manufacturing_orders.mapped('picking_ids').filtered(
lambda p: p.state == 'assigned'))
elif record.supply_method in ('purchase', 'outsourcing'):
assigned_picking_ids = record.sale_order_id.picking_ids.filtered(
lambda
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in record.product_id)
if record.supply_method == 'outsourcing':
outsourcing_assigned_picking_ids = record.get_outsourcing_picking_ids()
record.hide_action_outsourcing_stock_picking = outsourcing_assigned_picking_ids
record.hide_action_stock_picking = assigned_picking_ids or outsourcing_assigned_picking_ids
else:
record.hide_action_stock_picking = assigned_picking_ids
def get_outsourcing_picking_ids(self):
order_ids = self.env['purchase.order'].sudo().search(
[('order_line.product_id', 'in', self.product_id.ids),
('purchase_type', '=', 'outsourcing')])
outsourcing_picking_ids = order_ids._get_subcontracting_resupplies()
outsourcing_assigned_picking_ids = outsourcing_picking_ids.filtered(lambda p: p.state == 'assigned')
return outsourcing_assigned_picking_ids
def action_open_sale_order(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'res_model': 'sale.order',
'res_id': self.sale_order_id.id,
'view_mode': 'form',
}
def action_open_mrp_production(self):
self.ensure_one()
mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == self.product_id.id
)
action = {
'res_model': 'mrp.production',
'type': 'ir.actions.act_window',
}
if len(mrp_production_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': mrp_production_ids.id,
})
else:
action.update({
'name': _("制造订单列表"),
'domain': [('id', 'in', mrp_production_ids.ids)],
'view_mode': 'tree,form',
})
return action
def action_view_purchase_request(self):
self.ensure_one()
pr_ids = self.env['purchase.request'].sudo().search(
[('id', 'in', ast.literal_eval(self.outsourcing_purchase_request))])
action = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',
}
if len(pr_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': pr_ids[0].id,
})
else:
action.update({
'name': _("采购申请"),
'domain': [('id', 'in', pr_ids.ids)],
'view_mode': 'tree,form',
})
return action
def action_view_stock_picking(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
picking_ids = None
if self.supply_method in ('automation', 'manual'):
picking_ids = self.sale_order_id.mrp_production_ids.mapped('picking_ids').filtered(
lambda p: p.state == 'assigned')
elif self.supply_method in ('purchase', 'outsourcing'):
picking_ids = self.sale_order_id.picking_ids.filtered(
lambda
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in self.product_id)
if self.supply_method == 'outsourcing' and self.hide_action_outsourcing_stock_picking:
picking_ids = picking_ids.union(self.get_outsourcing_picking_ids())
if picking_ids:
if len(picking_ids) > 1:
action['domain'] = [('id', 'in', picking_ids.ids)]
elif picking_ids:
action['res_id'] = picking_ids.id
action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
if 'views' in action:
action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
return action
def action_view_programming(self):
self.ensure_one()
programming_mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
lambda p: p.programming_state == '编程中' and p.product_id.id == self.product_id.id
).mapped('programming_no')
if programming_mrp_production_ids:
programming_no = list(set(programming_mrp_production_ids))
numbers_str = "".join(programming_no)
raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")

View File

@@ -1,6 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sf_production_demand_plan,sf.production.demand.plan,model_sf_production_demand_plan,base.group_user,1,0,0,0
access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for dispatch,model_sf_production_demand_plan,sf_base.group_plan_dispatch,1,1,0,0
access_sf_demand_plan_print_wizard,sf.demand.plan.print.wizard,model_sf_demand_plan_print_wizard,base.group_user,1,0,0,0
access_sf_demand_plan_print_wizard_for_dispatch,sf.demand.plan.print.wizard for dispatch,model_sf_demand_plan_print_wizard,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
2 access_sf_production_demand_plan sf.production.demand.plan model_sf_production_demand_plan base.group_user 1 0 0 0
3 access_sf_production_demand_plan_for_dispatch sf.production.demand.plan for dispatch model_sf_production_demand_plan sf_base.group_plan_dispatch 1 1 0 0
4 access_sf_demand_plan_print_wizard sf.demand.plan.print.wizard model_sf_demand_plan_print_wizard base.group_user 1 0 0 0
5 access_sf_demand_plan_print_wizard_for_dispatch sf.demand.plan.print.wizard for dispatch model_sf_demand_plan_print_wizard sf_base.group_plan_dispatch 1 1 0 0

View File

@@ -1,11 +0,0 @@
.demand_plan_tree .o_list_table_ungrouped th:not(.o_list_record_selector,.row_no,[data-name=sequence]) {
min-width: 98px !important;
}
.demand_plan_tree .o_list_table_grouped th:not(.o_list_record_selector,.row_no,[data-name=sequence]) {
width: 98px !important;
}
.demand_plan_tree .o_list_table_ungrouped {
min-width: 1900px;
}

View File

@@ -1,119 +0,0 @@
<odoo>
<record id="view_sf_production_demand_plan_tree" model="ir.ui.view">
<field name="name">sf.production.demand.plan.tree</field>
<field name="model">sf.production.demand.plan</field>
<field name="arch" type="xml">
<tree string="需求计划" default_order="create_date desc" editable="bottom" class="demand_plan_tree">
<header>
<button string="打印" name="button_action_print" type="object"
class="btn-primary"/>
</header>
<field name="sequence" widget="handle"/>
<field name="id" optional="hide"/>
<field name="priority"/>
<field name="status"/>
<field name="partner_id"/>
<field name="order_remark"/>
<field name="glb_url" optional="hide"/>
<field name="product_id"/>
<field name="model_id" optional="hide"/>
<field name="part_name"/>
<field name="part_number"/>
<field name="is_incoming_material"/>
<field name="supply_method"/>
<field name="product_uom_qty"/>
<field name="deadline_of_delivery"/>
<field name="inventory_quantity_auto_apply"/>
<field name="qty_delivered"/>
<field name="qty_to_deliver"/>
<field name="model_long"/>
<field name="materials_id"/>
<field name="model_machining_precision"/>
<field name="model_process_parameters_ids" widget="many2many_tags"/>
<field name="product_remark" optional="hide"/>
<field name="order_code" optional="hide"/>
<field name="sale_order_id" optional="hide"/>
<field name="sale_order_line_id" optional="hide"/>
<field name="order_state"/>
<field name="route_id" optional="hide"/>
<field name="contract_date"/>
<field name="date_order"/>
<field name="contract_code"/>
<field name="plan_remark"/>
<field name="processing_time"/>
<field name="material_check"/>
<field name="hide_action_open_mrp_production" invisible="1"/>
<field name="hide_action_purchase_orders" invisible="1"/>
<field name="hide_action_stock_picking" invisible="1"/>
<field name="hide_action_view_programming" invisible="1"/>
<button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"
attrs="{'invisible': [('supply_method', '!=', False)]}"/>
<button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"
attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>
<button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"
attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>
<button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"
attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>
<button name="action_view_programming" type="object" string="编程单" class="btn-secondary"
attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>
<field name="planned_start_date"/>
<field name="actual_start_date"/>
<field name="actual_end_date"/>
<field name="create_date" optional="hide" string="创建时间"/>
<field name="create_uid" optional="hide" string="创建人"/>
<field name="write_date" string="更新时间"/>
<field name="write_uid" optional="hide" string="更新人"/>
<field name="print_count"/>
<button name="release_production_order" type="object" string="下达生产" class="btn-primary"
attrs="{'invisible': ['|',('status', '!=', '50'), ('supply_method', 'not in', ['automation', 'manual'])]}"/>
</tree>
</field>
</record>
<record id="view_sf_production_demand_plan_search" model="ir.ui.view">
<field name="name">sf.production.demand.plan.search</field>
<field name="model">sf.production.demand.plan</field>
<field name="arch" type="xml">
<search>
<field name="order_remark"/>
<field name="product_id"/>
<field name="part_name"/>
<field name="part_number"/>
<field name="partner_id"/>
<field name="supply_method"/>
<field name="materials_id"/>
<field name="model_process_parameters_ids"/>
<field name="plan_remark"/>
<group expand="0" string="Group By">
<filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/>
<filter name="group_by_status" string="状态" domain="[]" context="{'group_by': 'status'}"/>
<filter name="group_by_partner_id" string="客户" domain="[]" context="{'group_by': 'partner_id'}"/>
<filter name="group_by_is_incoming_material" string="客供料" domain="[]"
context="{'group_by': 'is_incoming_material'}"/>
<filter name="group_by_supply_method" string="供货方式" domain="[]"
context="{'group_by': 'supply_method'}"/>
<filter name="group_by_deadline_of_delivery" string="客户交期" domain="[]"
context="{'group_by': 'deadline_of_delivery'}"/>
<filter name="group_by_materials_id" string="材料" domain="[]"
context="{'group_by': 'materials_id'}"/>
</group>
</search>
</field>
</record>
<record id="sf_production_demand_plan_action" model="ir.actions.act_window">
<field name="name">需求计划</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.production.demand.plan</field>
<field name="view_mode">tree</field>
</record>
<menuitem
id="demand_plan_menu"
name="需求计划"
sequence="140"
action="sf_production_demand_plan_action"
parent="sf_plan.sf_production_plan_menu"
/>
</odoo>

View File

@@ -1 +0,0 @@
from . import sf_demand_plan_print_wizard

View File

@@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
import logging
from odoo import models, fields, api, _
_logger = logging.getLogger(__name__)
class SfDemandPlanPrintWizard(models.TransientModel):
_name = 'sf.demand.plan.print.wizard'
_description = u'打印向导'
demand_plan_id = fields.Many2one('sf.production.demand.plan', string='需求计划ID')
product_id = fields.Many2one(
comodel_name='product.product',
related='demand_plan_id.product_id',
string='产品', store=True, index=True)
model_id = fields.Char('模型ID')
filename_url = fields.Char('文件名/URL')
type = fields.Selection([
('1', '图纸'),
('2', '程序单'),
], string='类型')
status = fields.Selection([
('not_start', '未开始'),
('success', '成功'),
('fail', '失败'),
], string='状态', default='not_start')
machining_drawings = fields.Binary('2D加工图纸', related='product_id.machining_drawings', store=True)
workorder_id = fields.Many2one('mrp.workorder', string='工单')
cnc_worksheet = fields.Binary('程序单')
def demand_plan_print(self):
for record in self:
pdf_data = record.machining_drawings if record.type == '1' else record.cnc_worksheet
if pdf_data:
try:
# 执行打印
self.env['jikimo.printing'].sudo().print_pdf(pdf_data)
record.status = 'success'
t_part, c_part = record.demand_plan_id.print_count.split('C')
t_num = int(t_part[1:])
c_num = int(c_part)
if record.type == '1':
t_num += 1
elif record.type == '2':
c_num += 1
record.demand_plan_id.print_count = f"T{t_num}C{c_num}"
except Exception as e:
record.status = 'fail'
_logger.error(f"文件{record.filename_url}打印失败: {str(e)}")
class MrpWorkorder(models.Model):
_inherit = 'mrp.workorder'
def write(self, vals):
res = super(MrpWorkorder, self).write(vals)
for record in self:
if 'cnc_worksheet' in vals:
demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search(
[('workorder_id', '=', record.id)])
if demand_plan_print:
self.env['sf.demand.plan.print.wizard'].sudo().write(
{'cnc_worksheet': res.cnc_worksheet, 'filename_url': record.cnc_worksheet_name})
else:
demand_plan = self.env['sf.production.demand.plan'].sudo().search(
[('product_id', '=', record.product_id.id)])
if demand_plan:
wizard_vals = {
'demand_plan_id': demand_plan.id,
'model_id': demand_plan.model_id,
'type': '2',
'workorder_id': record.id,
'cnc_worksheet': record.cnc_worksheet,
'filename_url': record.cnc_worksheet_name
}
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
return res

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="action_plan_print_tree" model="ir.ui.view">
<field name="name">sf.demand.plan.print.wizard.tree</field>
<field name="model">sf.demand.plan.print.wizard</field>
<field name="arch" type="xml">
<tree string="打印">
<field name="model_id"/>
<field name="filename_url"/>
<field name="type"/>
<field name="machining_drawings" widget="adaptive_viewer"/>
<field name="cnc_worksheet" widget="pdf_viewer"/>
<field name="status"/>
</tree>
</field>
</record>
</odoo>

View File

@@ -17,7 +17,7 @@ class ResProductCategory(models.Model):
class ResProductProduct(models.Model): class ResProductProduct(models.Model):
_inherit = 'product.product' _inherit = 'product.product'
# single_manufacturing = fields.Boolean(string="单个制造") single_manufacturing = fields.Boolean(string="单个制造")
is_bfm = fields.Boolean('业务平台是否自动创建', default=False) is_bfm = fields.Boolean('业务平台是否自动创建', default=False)

View File

@@ -1,16 +1,16 @@
# import logging import logging
# from odoo import fields, models, api from odoo import fields, models, api
# from odoo.exceptions import UserError from odoo.exceptions import UserError
# from odoo.tools import str2bool from odoo.tools import str2bool
# class ResMrpRoutingWorkcenter(models.Model): class ResMrpRoutingWorkcenter(models.Model):
# _inherit = 'mrp.routing.workcenter' _inherit = 'mrp.routing.workcenter'
# def init(self): def init(self):
# super(ResMrpRoutingWorkcenter, self).init() super(ResMrpRoutingWorkcenter, self).init()
# # 在模块初始化时触发计算字段的更新 # 在模块初始化时触发计算字段的更新
# records = self.search([]) records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')): if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
# return return
# records.optional_process_parameters_date() records.optional_process_parameters_date()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True) self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)

View File

@@ -1,85 +1,85 @@
# # -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# import logging import logging
# from odoo import fields, models, api from odoo import fields, models, api
# from odoo.exceptions import UserError from odoo.exceptions import UserError
# from odoo.tools import str2bool from odoo.tools import str2bool
# class SfProductionProcessParameter(models.Model): class SfProductionProcessParameter(models.Model):
# _inherit = 'sf.production.process.parameter' _inherit = 'sf.production.process.parameter'
# @api.model @api.model
# def create(self, vals): def create(self, vals):
# # if vals.get('code', '/') == '/' or vals.get('code', '/') is False: # if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# # vals['code'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter') # vals['code'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter')
# if vals.get('routing_id'): if vals.get('routing_id'):
# # vals['gain_way'] = '外协' # vals['gain_way'] = '外协'
# routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id')) routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id'))
# if routing_id.surface_technics_id and not vals.get('process_id'): if routing_id.surface_technics_id and not vals.get('process_id'):
# vals['process_id'] = routing_id.surface_technics_id.id vals['process_id'] = routing_id.surface_technics_id.id
# if vals.get('code', '/') == '/' or vals.get('code', '/') is False: if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code( vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code(
# 'sf.production.process.parameter') 'sf.production.process.parameter')
# 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):
# 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()
# product_name = f"{self.process_id.name}_{self.name}" product_name = f"{self.process_id.name}_{self.name}"
# product_id = self.env['product.template'].search( product_id = self.env['product.template'].search(
# [("name", '=', product_name)]) [("name", '=', product_name)])
# if product_id: if product_id:
# product_id.server_product_process_parameters_id = self.id product_id.server_product_process_parameters_id = self.id
# else: else:
# res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')]) res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')])
# self.env['product.template'].create({ self.env['product.template'].create({
# 'detailed_type': 'service', 'detailed_type': 'service',
# 'name': product_name, 'name': product_name,
# 'invoice_policy': 'delivery', 'invoice_policy': 'delivery',
# 'categ_id': service_categ.id, 'categ_id': service_categ.id,
# 'description': f"基于{self.name}创建的服务产品", 'description': f"基于{self.name}创建的服务产品",
# 'sale_ok': True, # 可销售 'sale_ok': True, # 可销售
# 'purchase_ok': True, # 可采购 'purchase_ok': True, # 可采购
# 'server_product_process_parameters_id': self.id, 'server_product_process_parameters_id': self.id,
# 'seller_ids': [(0, 0, { 'seller_ids': [(0, 0, {
# # 'delay': 1, # 'delay': 1,
# 'partner_id': res_partner.id, 'partner_id': res_partner.id,
# 'price': 1, })], 'price': 1, })],
# }) })
# def create_work_center(self): def create_work_center(self):
# production_process_parameter = self production_process_parameter = self
# if not production_process_parameter.process_id: if not production_process_parameter.process_id:
# return return
# if not production_process_parameter.routing_id: if not production_process_parameter.routing_id:
# workcenter_id = self.env['mrp.routing.workcenter'].search( workcenter_id = self.env['mrp.routing.workcenter'].search(
# [("surface_technics_id", '=', production_process_parameter.process_id.id)]) [("surface_technics_id", '=', production_process_parameter.process_id.id)])
# if not workcenter_id: if not workcenter_id:
# outsourcing_work_center = self.env['mrp.workcenter'].search( outsourcing_work_center = self.env['mrp.workcenter'].search(
# [("name", '=', '外协工作中心')]) [("name", '=', '外协工作中心')])
# routing_id = self.env['mrp.routing.workcenter'].create({ routing_id = self.env['mrp.routing.workcenter'].create({
# 'workcenter_ids': [(6, 0, outsourcing_work_center.ids)], 'workcenter_ids': [(6, 0, outsourcing_work_center.ids)],
# 'routing_tag': 'special', 'routing_tag': 'special',
# 'routing_type': '表面工艺', 'routing_type': '表面工艺',
# 'is_outsource': True, 'is_outsource': True,
# 'surface_technics_id': production_process_parameter.process_id.id, 'surface_technics_id': production_process_parameter.process_id.id,
# 'name': production_process_parameter.process_id.name, 'name': production_process_parameter.process_id.name,
# }) })
# production_process_parameter.routing_id = routing_id.id production_process_parameter.routing_id = routing_id.id
# else: else:
# production_process_parameter.routing_id = workcenter_id.id production_process_parameter.routing_id = workcenter_id.id
# def init(self): def init(self):
# super(SfProductionProcessParameter, self).init() super(SfProductionProcessParameter, self).init()
# # 在模块初始化时触发计算字段的更新 # 在模块初始化时触发计算字段的更新
# records = self.search([]) records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process', if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process',
# default='False')): default='False')):
# return return
# for record in records: for record in records:
# if not record.outsourced_service_products: if not record.outsourced_service_products:
# record.create_service_product() record.create_service_product()
# record.create_work_center() record.create_work_center()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True) self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True)

View File

@@ -20,6 +20,7 @@
'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',
@@ -48,7 +49,7 @@
'views/mrp_workorder_batch_replan.xml', 'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml', 'views/purchase_order_view.xml',
'views/product_template_views.xml', 'views/product_template_views.xml',
# 'views/stock_warehouse_orderpoint.xml', 'views/stock_warehouse_orderpoint.xml',
], ],
'assets': { 'assets': {

View File

@@ -2,16 +2,18 @@
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='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', 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):
@@ -54,7 +56,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='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('获取日计划', requester='中控系统') @api_log('获取日计划', requester='中控系统')
def get_ShiftPlan(self, **kw): def get_ShiftPlan(self, **kw):
@@ -108,7 +110,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='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/QcCheck', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工件预调(前置三元检测)', requester='中控系统') @api_log('工件预调(前置三元检测)', requester='中控系统')
def get_qcCheck(self, **kw): def get_qcCheck(self, **kw):
@@ -704,3 +706,59 @@ 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

@@ -0,0 +1,16 @@
<?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

@@ -18,4 +18,4 @@ from . import quick_easy_order
from . import purchase_order from . import purchase_order
from . import quality_check from . import quality_check
from . import purchase_request_line from . import purchase_request_line
# from . import stock_warehouse_orderpoint from . import stock_warehouse_orderpoint

View File

@@ -279,7 +279,7 @@ class MrpProduction(models.Model):
production_id.part_name = production_id.product_id.part_name production_id.part_name = production_id.product_id.part_name
elif production_id.product_id.categ_id.type == '坯料': elif production_id.product_id.categ_id.type == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', production_id.product_id.name) match = re.search(r'(S\d{5}-\d)', production_id.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -900,41 +900,41 @@ class MrpProduction(models.Model):
for workorder in production.workorder_ids: for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected() workorder.duration_expected = workorder._get_duration_expected()
# def _create_subcontract_purchase_request(self, purchase_request_line): def _create_subcontract_purchase_request(self, purchase_request_line):
# sorted_list = sorted(purchase_request_line, key=itemgetter('name')) sorted_list = sorted(purchase_request_line, key=itemgetter('name'))
# grouped_purchase_request_line = { grouped_purchase_request_line = {
# k: list(g) k: list(g)
# for k, g in groupby(sorted_list, key=itemgetter('name')) for k, g in groupby(sorted_list, key=itemgetter('name'))
# } }
# for name, request_line in grouped_purchase_request_line.items(): for name, request_line in grouped_purchase_request_line.items():
# request_line_sorted_list = sorted(request_line, key=itemgetter('product_id')) request_line_sorted_list = sorted(request_line, key=itemgetter('product_id'))
# grouped_purchase_request_line_sorted_list = { grouped_purchase_request_line_sorted_list = {
# k: list(g) k: list(g)
# for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id')) for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id'))
# } }
# purchase_request_model = self.env["purchase.request"] purchase_request_model = self.env["purchase.request"]
# origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')}) origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')})
# pr = purchase_request_model.create({ pr = purchase_request_model.create({
# "origin": origin, "origin": origin,
# "company_id": self.company_id.id, "company_id": self.company_id.id,
# "picking_type_id": self.env.ref('stock.picking_type_in').id, "picking_type_id": self.env.ref('stock.picking_type_in').id,
# "group_id": request_line[0].get('group_id'), "group_id": request_line[0].get('group_id'),
# "requested_by": self.env.context.get("uid", self.env.uid), "requested_by": self.env.context.get("uid", self.env.uid),
# "assigned_to": False, "assigned_to": False,
# "bom_id": self[0].bom_id.id, "bom_id": self[0].bom_id.id,
# "is_subcontract":True, "is_subcontract":True,
# }) })
# self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref( self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref(
# '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]
# # 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')})
# cur_request_line.pop('group_id', None) cur_request_line.pop('group_id', None)
# cur_request_line.pop('production_name', None) cur_request_line.pop('production_name', None)
# self.env["purchase.request.line"].create(cur_request_line) self.env["purchase.request.line"].create(cur_request_line)
# pr.button_approved() pr.button_approved()
# 外协出入库单处理 # 外协出入库单处理
def get_subcontract_pick_purchase(self): def get_subcontract_pick_purchase(self):
@@ -962,14 +962,14 @@ class MrpProduction(models.Model):
if not sorted_workorders: if not sorted_workorders:
return return
for workorders in reversed(sorted_workorders): for workorders in reversed(sorted_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)
# purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request( purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request(
# workorders, production) workorders, production)
# all_workorders += workorders all_workorders += workorders
# self._create_subcontract_purchase_request(purchase_request_line) self._create_subcontract_purchase_request(purchase_request_line)
# for workorder in all_workorders: for workorder in all_workorders:
# workorder._compute_pr_mp_count() workorder._compute_pr_mp_count()
# 工单排序 # 工单排序
def _reset_work_order_sequence1(self, k): def _reset_work_order_sequence1(self, k):
for rec in self: for rec in self:

View File

@@ -1,7 +1,7 @@
import logging import logging
from odoo import fields, models, api from odoo import fields, models, api
from odoo.exceptions import UserError from odoo.exceptions import UserError
# from odoo.tools import str2bool from odoo.tools import str2bool
class ResMrpRoutingWorkcenter(models.Model): class ResMrpRoutingWorkcenter(models.Model):
@@ -25,20 +25,20 @@ class ResMrpRoutingWorkcenter(models.Model):
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True) workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False) bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺") surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
# optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数') optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数')
reserved_duration = fields.Float('预留时长', default=30, tracking=True) reserved_duration = fields.Float('预留时长', default=30, tracking=True)
is_outsource = fields.Boolean('外协', default=False) is_outsource = fields.Boolean('外协', default=False)
individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录') individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录')
# @api.onchange('surface_technics_id') @api.onchange('surface_technics_id')
# def optional_process_parameters_date(self): def optional_process_parameters_date(self):
# for record in self: for record in self:
# if not record.surface_technics_id: if not record.surface_technics_id:
# continue continue
# parameter_ids = self.env['sf.production.process.parameter'].search([ parameter_ids = self.env['sf.production.process.parameter'].search([
# ('process_id', '=', record.surface_technics_id.id), ('process_id', '=', record.surface_technics_id.id),
# ]) ])
# record.optional_process_parameters = parameter_ids.ids record.optional_process_parameters = parameter_ids.ids
# @api.model # @api.model
# def _auto_init(self): # def _auto_init(self):

View File

@@ -21,16 +21,16 @@ class ResWorkcenter(models.Model):
related='equipment_id.production_line_id', store=True) related='equipment_id.production_line_id', store=True)
is_process_outsourcing = fields.Boolean('工艺外协') is_process_outsourcing = fields.Boolean('工艺外协')
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True) users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
# @api.constrains('name') @api.constrains('name')
# def _check_unique_name_code(self): def _check_unique_name_code(self):
# for record in self: for record in self:
# # 检查是否已经存在相同的 name 和 code 组合 # 检查是否已经存在相同的 name 和 code 组合
# existing = self.search([ existing = self.search([
# ('name', '=', record.name), ('name', '=', record.name),
# ('id', '!=', record.id) # 排除当前记录 ('id', '!=', record.id) # 排除当前记录
# ]) ])
# if existing: if existing:
# raise ValueError('记录已存在') raise ValueError('记录已存在')
def write(self, vals): def write(self, vals):
if 'users_ids' in vals: if 'users_ids' in vals:
old_users = self.users_ids old_users = self.users_ids

View File

@@ -6,6 +6,7 @@ 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
@@ -70,21 +71,21 @@ class ResMrpWorkOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True) tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True) back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
# pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True) pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
# @api.depends('state') @api.depends('state')
# def _compute_pr_mp_count(self): def _compute_pr_mp_count(self):
# for item in self: for item in self:
# if not item.is_subcontract: if not item.is_subcontract:
# item.pr_mp_count = 0 item.pr_mp_count = 0
# continue continue
# pr_ids = self.env['purchase.request'].sudo().search( pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'), [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')]) ('state', '!=', 'rejected')])
# if pr_ids: if pr_ids:
# item.pr_mp_count = len(pr_ids) item.pr_mp_count = len(pr_ids)
# else: else:
# item.pr_mp_count = 0 item.pr_mp_count = 0
@api.depends('state') @api.depends('state')
def _compute_back_button_display(self): def _compute_back_button_display(self):
@@ -130,14 +131,8 @@ class ResMrpWorkOrder(models.Model):
record.back_button_display = False record.back_button_display = False
else: else:
next_workorder = sorted_workorders[position + 1] next_workorder = sorted_workorders[position + 1]
# 持续获取下一个工单,直到找到一个不是返工的工单 next_state = next_workorder.state
while next_workorder and next_workorder.state == 'rework': if (next_state == 'ready' or (
position += 1
if position + 1 < len(sorted_workorders):
next_workorder = sorted_workorders[position + 1]
else:
next_workorder = None
if next_workorder and (next_workorder.state == 'ready' or (
next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done': next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done':
record.back_button_display = True record.back_button_display = True
else: else:
@@ -326,7 +321,6 @@ class ResMrpWorkOrder(models.Model):
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True) tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
technology_design_id = fields.Many2one('sf.technology.design') technology_design_id = fields.Many2one('sf.technology.design')
cnc_worksheet_name = fields.Char('工作指令文件名', readonly=True)
def _compute_default_construction_period_status(self): def _compute_default_construction_period_status(self):
need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected', 'done'] need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected', 'done']
@@ -447,15 +441,15 @@ class ResMrpWorkOrder(models.Model):
action['context'] = dict(self._context, default_origin=self.name) action['context'] = dict(self._context, default_origin=self.name)
return action return action
@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']: if order.routing_type == '表面工艺' and order.state not in ['cancel']:
# domain = [('group_id', '=', self.production_id.procurement_group_id.id),
# ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')]
domain = [('purchase_type', '=', 'consignment'), domain = [('purchase_type', '=', 'consignment'),
('origin', 'like', '%' + self.production_id.name + '%'), ('origin', 'like', '%' + self.production_id.name + '%'),
('state', '!=', 'cancel')] ('state', '!=', 'cancel')]
# domain = [('purchase_type', '=', 'consignment'),
# ('origin', 'like', '%' + self.production_id.name + '%'),
# ('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain) purchase = self.env['purchase.order'].search(domain)
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
if not purchase: if not purchase:
@@ -468,30 +462,30 @@ class ResMrpWorkOrder(models.Model):
else: else:
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
# def action_view_pr_mrp_workorder(self): def action_view_pr_mrp_workorder(self):
# """ """
# 采购请求 采购请求
# """ """
# self.ensure_one() self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search( pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'), [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')]) ('state', '!=', 'rejected')])
# action = { action = {
# 'res_model': 'purchase.request', 'res_model': 'purchase.request',
# 'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
# } }
# if len(pr_ids) == 1: if len(pr_ids) == 1:
# action.update({ action.update({
# 'view_mode': 'form', 'view_mode': 'form',
# 'res_id': pr_ids[0].id, 'res_id': pr_ids[0].id,
# }) })
# else: else:
# action.update({ action.update({
# 'name': _("从 %s生成采购请求单", self.name), 'name': _("%s生成采购请求单", self.name),
# 'domain': [('id', 'in', pr_ids)], 'domain': [('id', 'in', pr_ids)],
# 'view_mode': 'tree,form', 'view_mode': 'tree,form',
# }) })
# return action return action
def action_view_surface_technics_purchase(self): def action_view_surface_technics_purchase(self):
self.ensure_one() self.ensure_one()
@@ -778,146 +772,170 @@ 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:
paload_data = { base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
"filename": local_filename paload_data = {
} "filename": local_filename,
if not ftp_resconfig['get_check_file_path']: "sf_host": base_url
raise UserError('请先配置获取检测报告地址') }
url = ftp_resconfig['get_check_file_path'] + '/get/check/report' if not ftp_resconfig['get_check_file_path']:
response = requests.post(url, json=paload_data) raise UserError('请先配置获取检测报告地址')
logging.info('response:%s' % response.json()) url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
if response.json().get('detail'): response = requests.post(url, json=paload_data)
raise UserError(response.json().get('detail')) # logging.info('response:%s' % response.json())
# 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' # 使用下载的实际文件路径 document = self.env['documents.document'].sudo().search([('res_model', '=', 'mrp.workorder'), ('res_id', '=', self.id)])
parser = etree.XMLParser(recover=True) # Using recover to handle errors if not document:
tree = etree.parse(local_file_path, parser) raise UserError(f"未获取到检测数据")
logging.info('tree:%s' % tree) binary_data = base64.b64decode(document.attachment_id.datas)
root = tree.getroot()
logging.info('root:%s' % root)
# 准备一个外部字典来存储以PT为键的坐标字典 # 创建临时文件保存响应内容
pt_coordinates = {} with tempfile.NamedTemporaryFile(delete=False, suffix='.xls') as temp_file:
# 遍历每个工作表和行 temp_file.write(binary_data)
for worksheet in root.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Worksheet'): temp_file_path = temp_file.name
sheet_name = worksheet.attrib.get('{urn:schemas-microsoft-com:office:spreadsheet}Name')
logging.info('sheet_name:%s' % sheet_name) try:
if sheet_name == "Sheet1": # 确保我们只查看包含数据的工作表 # 使用临时文件进行解析
current_pt = None parser = etree.XMLParser(recover=True)
for row in worksheet.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Row'): tree = etree.parse(temp_file_path, parser)
cells = list(row.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Cell')) logging.info('tree:%s' % tree)
for i, cell in enumerate(cells): root = tree.getroot()
data_cell = cell.find('.//{urn:schemas-microsoft-com:office:spreadsheet}Data') logging.info('root:%s' % root)
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等键 # 准备一个外部字典来存储以PT为键的坐标字典
if 'PT1' in pt_coordinates and pt_coordinates['PT1']: pt_coordinates = {}
self.X1_axis = pt_coordinates['PT3'][0]['X'] # 遍历每个工作表和行
self.Y1_axis = pt_coordinates['PT3'][1]['Y'] for worksheet in root.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Worksheet'):
self.Z1_axis = pt_coordinates['PT3'][2]['Z'] sheet_name = worksheet.attrib.get('{urn:schemas-microsoft-com:office:spreadsheet}Name')
else: logging.info('sheet_name:%s' % sheet_name)
raise UserError('PT1点未测或数据错误') if sheet_name == "Sheet1": # 确保我们只查看包含数据的工作表
if 'PT2' in pt_coordinates and pt_coordinates['PT2']: current_pt = None
self.X2_axis = pt_coordinates['PT4'][0]['X'] for row in worksheet.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Row'):
self.Y2_axis = pt_coordinates['PT4'][1]['Y'] cells = list(row.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Cell'))
self.Z2_axis = pt_coordinates['PT4'][2]['Z'] for i, cell in enumerate(cells):
else: data_cell = cell.find('.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
raise UserError('PT2点未测或数据错误') if data_cell is not None and data_cell.text is not None: # 添加检查以确保data_cell.text不为空
if 'PT3' in pt_coordinates and pt_coordinates['PT3']: # 检查是否是PT标识
self.X3_axis = pt_coordinates['PT5'][0]['X'] logging.info(f"Data in cell: {data_cell.text}") # 输出单元格数据
self.Y3_axis = pt_coordinates['PT5'][1]['Y'] if "PT" in data_cell.text:
self.Z3_axis = pt_coordinates['PT5'][2]['Z'] current_pt = data_cell.text
else: pt_coordinates[current_pt] = []
raise UserError('PT3点未测或数据错误') elif data_cell.text in ["X", "Y", "Z"] and current_pt is not None:
if 'PT4' in pt_coordinates and pt_coordinates['PT4']: # 确保当前单元格后面还有单元格存在,以获取理论值
self.X4_axis = pt_coordinates['PT6'][0]['X'] if i + 1 < len(cells):
self.Y4_axis = pt_coordinates['PT6'][1]['Y'] next_cell = cells[i + 1]
self.Z4_axis = pt_coordinates['PT6'][2]['Z'] theory_value = next_cell.find(
else: './/{urn:schemas-microsoft-com:office:spreadsheet}Data')
raise UserError('PT4点未测或数据错误') if theory_value is not None:
if 'PT5' in pt_coordinates and pt_coordinates['PT5']: # 为当前PT键添加坐标数据
self.X5_axis = pt_coordinates['PT7'][0]['X'] pt_coordinates[current_pt].append({
self.Y5_axis = pt_coordinates['PT7'][1]['Y'] data_cell.text: float(theory_value.text)
self.Z5_axis = pt_coordinates['PT7'][2]['Z'] })
else: logging.info(f"PT: {current_pt} - {data_cell.text}: {theory_value.text}")
raise UserError('PT5点未测或数据错误') logging.info('pt_coordinates=====%s' % pt_coordinates)
if 'PT6' in pt_coordinates and pt_coordinates['PT6']: # pt_coordinates:{'PT1': [{'X': 38.9221}, {'Y': -18.7304}, {'Z': 128.0783}],
self.X6_axis = pt_coordinates['PT8'][0]['X'] # 'PT2': [{'X': 39.2456}, {'Y': -76.9169}, {'Z': 123.7541}]}
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 # 检查是否存在PT1等键
self.getcenter() 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点未测或数据错误')
return True 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')
@@ -1252,12 +1270,12 @@ class ResMrpWorkOrder(models.Model):
}] }]
return workorders_values_str return workorders_values_str
# def check_lot_exists(self, picking_id, lot_id): def check_lot_exists(self, picking_id, lot_id):
# return bool( return bool(
# picking_id.move_ids.move_line_ids.filtered( picking_id.move_ids.move_line_ids.filtered(
# lambda line: line.lot_id.id == lot_id lambda line: line.lot_id.id == lot_id
# ) )
# ) )
def _process_compute_state(self): def _process_compute_state(self):
sorted_workorders = sorted(self, key=lambda x: x.sequence) sorted_workorders = sorted(self, key=lambda x: x.sequence)
@@ -1299,10 +1317,10 @@ class ResMrpWorkOrder(models.Model):
purchase_orders_id = self._get_surface_technics_purchase_ids() purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase': if purchase_orders_id.state == 'purchase':
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 mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
@@ -1341,11 +1359,10 @@ class ResMrpWorkOrder(models.Model):
if purchase_orders_id: if purchase_orders_id:
if purchase_orders_id.state == 'purchase': if purchase_orders_id.state == 'purchase':
workorder.state = 'ready' workorder.state = 'ready'
move_out = workorder.move_subcontract_workorder_ids[1] picking_id = workorder.production_id.picking_ids.filtered(
# picking_id = workorder.production_id.picking_ids.filtered( lambda
# lambda 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 mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
@@ -1419,10 +1436,9 @@ class ResMrpWorkOrder(models.Model):
# 表面工艺外协出库单 # 表面工艺外协出库单
if self.routing_type == '表面工艺': if self.routing_type == '表面工艺':
if self.is_subcontract is True: if self.is_subcontract is True:
move_out = self.move_subcontract_workorder_ids[1] picking_id = self.production_id.picking_ids.filtered(
# picking_id = self.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 = self.move_subcontract_workorder_ids[1] # move_out = self.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search( # move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search( # [('location_id', '=', self.env['stock.location'].search(

View File

@@ -795,7 +795,7 @@ class ResProductMo(models.Model):
for record in self: for record in self:
if record.categ_id.name == '坯料': if record.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', record.name) match = re.search(r'(S\d{5}-\d)', record.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)

View File

@@ -59,83 +59,83 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)]) production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id) purchase.production_count = len(production_id)
# def process_replenish(self,production,total_qty): def process_replenish(self,production,total_qty):
# record = self record = self
# bom_line_id = production.bom_id.bom_line_ids bom_line_id = production.bom_id.bom_line_ids
# replenish = self.env['stock.warehouse.orderpoint'].search([ replenish = self.env['stock.warehouse.orderpoint'].search([
# ('product_id', '=', bom_line_id.product_id.id), ('product_id', '=', bom_line_id.product_id.id),
# ( (
# 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id), 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id),
# # ('state', 'in', ['draft', 'confirmed']) # ('state', 'in', ['draft', 'confirmed'])
# ], limit=1) ], limit=1)
# if not replenish: if not replenish:
# replenish_model = self.env['stock.warehouse.orderpoint'] replenish_model = self.env['stock.warehouse.orderpoint']
# replenish = replenish_model.create({ replenish = replenish_model.create({
# 'product_id': bom_line_id.product_id.id, 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref( 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id, 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id, 'group_id': record.group_id.id,
# 'qty_to_order': total_qty, 'qty_to_order': total_qty,
# 'origin': record.name, 'origin': record.name,
# }) })
# else: else:
# replenish.write({ replenish.write({
# 'product_id': bom_line_id.product_id.id, 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref( 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id, 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id, 'group_id': record.group_id.id,
# 'qty_to_order': total_qty + replenish.qty_to_order, 'qty_to_order': total_qty + replenish.qty_to_order,
# 'origin': record.name + ',' + replenish.origin, 'origin': record.name + ',' + replenish.origin,
# }) })
# replenish.action_replenish() replenish.action_replenish()
# def outsourcing_service_replenishment(self): def outsourcing_service_replenishment(self):
# record = self record = self
# if record.purchase_type != 'consignment': if record.purchase_type != 'consignment':
# return return
# grouped_lines = {} grouped_lines = {}
# for line in record.order_line: for line in record.order_line:
# if line.related_product.id not in grouped_lines: if line.related_product.id not in grouped_lines:
# grouped_lines[line.related_product.id] = [] grouped_lines[line.related_product.id] = []
# grouped_lines[line.related_product.id].append(line) grouped_lines[line.related_product.id].append(line)
# for product_id,lines in grouped_lines.items(): for product_id,lines in grouped_lines.items():
# production = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) production = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# if not production: if not production:
# continue continue
# total_qty = sum(line.product_qty for line in lines) total_qty = sum(line.product_qty for line in lines)
# record.process_replenish(production,total_qty) record.process_replenish(production,total_qty)
# for product_id,lines in grouped_lines.items(): for product_id,lines in grouped_lines.items():
# productions = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) productions = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# if not productions: if not productions:
# continue continue
# # production.bom_id.bom_line_ids.product_id # production.bom_id.bom_line_ids.product_id
# location_id = self.env['stock.location'].search([('name', '=', '制造前')]) location_id = self.env['stock.location'].search([('name', '=', '制造前')])
# quants = self.env['stock.quant'].search([ quants = self.env['stock.quant'].search([
# ('product_id', '=', productions.bom_id.bom_line_ids.product_id.id), ('product_id', '=', productions.bom_id.bom_line_ids.product_id.id),
# ('location_id', '=', location_id.id) ('location_id', '=', location_id.id)
# ]) ])
# total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量 total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量
# is_available = total_qty > 0 is_available = total_qty > 0
# if not is_available: if not is_available:
# raise UserError('请先完成坯料入库') raise UserError('请先完成坯料入库')
# for production_id in productions: for production_id in productions:
# work_ids = production_id.workorder_ids.filtered( work_ids = production_id.workorder_ids.filtered(
# lambda wk: wk.state not in ['done', 'rework', 'cancel']) lambda wk: wk.state not in ['done', 'rework', 'cancel'])
# 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:
# 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 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:
# self.env['stock.move.line'].create( self.env['stock.move.line'].create(
# mo.get_move_line(production_id, min_sequence_wk)) mo.get_move_line(production_id, min_sequence_wk))
# product = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) # product = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# match = re.search(r'(S\d{5}-\d)',product.name) # match = re.search(r'(S\d{5}-\d)',product.name)
# pass # pass
@@ -146,7 +146,7 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入') raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0: if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入') raise UserError('请对【产品】中的【单价】进行输入')
# record.outsourcing_service_replenishment() record.outsourcing_service_replenishment()
res = super(PurchaseOrder, self).button_confirm() res = super(PurchaseOrder, self).button_confirm()
@@ -208,7 +208,7 @@ class PurchaseOrderLine(models.Model):
continue continue
if record.product_id.categ_id.name == '坯料': if record.product_id.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', record.product_id.name) match = re.search(r'(S\d{5}-\d)', record.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)

View File

@@ -1,30 +1,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# import base64 import base64
# import datetime import datetime
# import logging import logging
# import json import json
# import os import os
# import re import re
# import traceback import traceback
# from operator import itemgetter from operator import itemgetter
# 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.tools import float_compare, float_round, float_is_zero, format_datetime from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
# class PurchaseRequestLine(models.Model): class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request' _inherit = 'purchase.request'
# is_subcontract = fields.Boolean(string='是否外协',default=False) is_subcontract = fields.Boolean(string='是否外协',default=False)
# class PurchaseRequestLine(models.Model): class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request.line' _inherit = 'purchase.request.line'
# is_subcontract = fields.Boolean(string='是否外协') is_subcontract = fields.Boolean(string='是否外协')
# class PurchaseRequest(models.Model): class PurchaseRequest(models.Model):
# _inherit = 'purchase.request' _inherit = 'purchase.request'
# bom_id = fields.Many2one('mrp.bom') bom_id = fields.Many2one('mrp.bom')

View File

@@ -56,7 +56,7 @@ class SaleOrder(models.Model):
'jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo() 'jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo()
# 复制成品模板上的属性 # 复制成品模板上的属性
line.product_id.product_tmpl_id.copy_template(product_template_id) line.product_id.copy_template(product_template_id)
# 将模板上的single_manufacturing属性复制到成品上 # 将模板上的single_manufacturing属性复制到成品上
# line.product_id.single_manufacturing = product_template_id.single_manufacturing # line.product_id.single_manufacturing = product_template_id.single_manufacturing
# line.product_id.tracking = product_template_id.tracking # line.product_id.tracking = product_template_id.tracking
@@ -76,7 +76,7 @@ class SaleOrder(models.Model):
'embryo_redundancy_id': line.embryo_redundancy_id, 'embryo_redundancy_id': line.embryo_redundancy_id,
} }
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', product.name) match = re.search(r'(S\d{5}-\d*)', product.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)

View File

@@ -7,74 +7,74 @@ from odoo.exceptions import UserError, ValidationError
class SfProductionProcessParameter(models.Model): class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter' _inherit = 'sf.production.process.parameter'
# service_products = fields.Many2one( service_products = fields.Many2one(
# 'product.template', 'product.template',
# string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products', string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products',
# store=True store=True
# ) )
# outsourced_service_products = fields.One2many( outsourced_service_products = fields.One2many(
# 'product.template', # 另一个模型的名称 'product.template', # 另一个模型的名称
# 'server_product_process_parameters_id', # 对应的 Many2one 字段名称 'server_product_process_parameters_id', # 对应的 Many2one 字段名称
# string='外协服务产品' string='外协服务产品'
# ) )
# 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') @api.depends('outsourced_service_products')
# def _compute_service_products(self): def _compute_service_products(self):
# for record in self: for record in self:
# # 假设取第一条作为主明细 # 假设取第一条作为主明细
# record.service_products = record.outsourced_service_products.ids if record.outsourced_service_products else False 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:
# # 确保关联关系正确 # 确保关联关系正确
# 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
# def name_get(self): def name_get(self):
# result = [] result = []
# for record in self: for record in self:
# name = f"{record.process_id.name} - {record.name}" # 自定义显示格式 name = f"{record.process_id.name} - {record.name}" # 自定义显示格式
# result.append((record.id, name)) result.append((record.id, name))
# return result return result
# @api.constrains('outsourced_service_products') @api.constrains('outsourced_service_products')
# def _validate_partner_limit(self): def _validate_partner_limit(self):
# for record in self: for record in self:
# if len(record.outsourced_service_products) > 1: if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联") raise ValidationError("工艺参数不能与多个产品关联")
# @api.onchange('outsourced_service_products') @api.onchange('outsourced_service_products')
# def _onchange_validate_partner_limit(self): def _onchange_validate_partner_limit(self):
# for record in self: for record in self:
# if len(record.outsourced_service_products) > 1: if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联") raise ValidationError("工艺参数不能与多个产品关联")
# @api.depends('outsourced_service_products') @api.depends('outsourced_service_products')
# def _compute_is_product_button(self): def _compute_is_product_button(self):
# for record in self: for record in self:
# if record.outsourced_service_products: if record.outsourced_service_products:
# record.is_product_button = True record.is_product_button = True
# else: else:
# record.is_product_button = False record.is_product_button = False
# def has_wksp_prefix(self): def has_wksp_prefix(self):
# """ """
# 判断字符串是否以WKSP开头不区分大小写 判断字符串是否以WKSP开头不区分大小写
# :param text: 要检查的字符串 :param text: 要检查的字符串
# :return: True/False :return: True/False
# """ """
# return self.code.upper().startswith('101'+self.routing_id.code) return self.code.upper().startswith('101'+self.routing_id.code)
# @api.depends('outsourced_service_products','code') @api.depends('outsourced_service_products','code')
# def _compute_is_delete_button(self): def _compute_is_delete_button(self):
# for record in self: for record in self:
# if record.outsourced_service_products and record.has_wksp_prefix(): if record.outsourced_service_products and record.has_wksp_prefix():
# record.is_delete_button = False record.is_delete_button = False
# elif record.outsourced_service_products: elif record.outsourced_service_products:
# record.is_delete_button = True record.is_delete_button = True
# else: else:
# record.is_delete_button = True record.is_delete_button = True
@api.model @api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('route_id'): if self._context.get('route_id'):
@@ -90,19 +90,19 @@ class SfProductionProcessParameter(models.Model):
return self._search(domain, limit=limit, access_rights_uid=name_get_uid) return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid) return super()._name_search(name, args, operator, limit, name_get_uid)
# def action_create_service_product(self): def action_create_service_product(self):
# if self.id: # 如果是已存在的记录 if self.id: # 如果是已存在的记录
# self.write({}) # 空写入会触发保存 self.write({}) # 空写入会触发保存
# else: # 如果是新记录 else: # 如果是新记录
# self = self.create(self._convert_to_write(self.read()[0])) self = self.create(self._convert_to_write(self.read()[0]))
# return { return {
# 'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
# 'name': '向导名称', 'name': '向导名称',
# 'res_model': 'product.creation.wizard', 'res_model': 'product.creation.wizard',
# 'view_mode': 'form', 'view_mode': 'form',
# 'target': 'new', 'target': 'new',
# 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID
# } }
# #
# return { # return {
# 'name': '创建服务产品', # 'name': '创建服务产品',
@@ -116,6 +116,6 @@ class SfProductionProcessParameter(models.Model):
# }, # },
# } # }
# def action_hide_service_products(self): def action_hide_service_products(self):
# # self.outsourced_service_products.active = False # self.outsourced_service_products.active = False
# self.active = False self.active = False

View File

@@ -564,13 +564,6 @@ class StockPicking(models.Model):
part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True) part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True)
part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True) part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True)
model_id = fields.Char('模型ID', compute='_compute_model_id', store=True, index=True)
@api.depends('move_ids_without_package.model_id')
def _compute_model_id(self):
for picking in self:
model_id = picking.move_ids_without_package.mapped('model_id')
picking.model_id = ','.join(filter(None, model_id))
@api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name') @api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name')
def _compute_part_info(self): def _compute_part_info(self):
@@ -638,84 +631,62 @@ class StockPicking(models.Model):
move.action_clear_lines_show_details() move.action_clear_lines_show_details()
move.action_show_details() move.action_show_details()
res = super().button_validate() res = super().button_validate()
# lot_ids = None lot_ids = None
# product_ids = self.move_ids.mapped('product_id') product_ids = self.move_ids.mapped('product_id')
# if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none': if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
# lot_ids = self.move_ids.move_line_ids.mapped('lot_id') lot_ids = self.move_ids.move_line_ids.mapped('lot_id')
# production_ids = self.sale_order_id.mrp_production_ids if self.sale_order_id else self.env['mrp.production'] production_ids = self.sale_order_id.mrp_production_ids if self.sale_order_id else self.env['mrp.production']
# if res and self.location_id.name == '外协收料区' and self.location_dest_id.name == '制造前': if res and self.location_id.name == '外协收料区' and self.location_dest_id.name == '制造前':
# # 如果是最后一张外协入库单,则设置库存位置的预留数量
# for production_id in production_ids:
# if lot_ids:
# lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if lot_id in lot_ids:
# workorder_id = production_id.workorder_ids.filtered(
# lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# else:
# workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# # lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = workorder.production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# # if move_in:
# # workorder = move_in.subcontract_workorder_id
# # workorders = workorder.production_id.workorder_ids
# # subcontract_workorders = workorders.filtered(
# # lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
# # # if workorder == subcontract_workorders[-1]:
# # # 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,
# # # package_id=False, owner_id=False, strict=False
# # # )
# # workorder.button_finish()
# if res and self.location_id.name == '制造前' and self.location_dest_id.name == '外协加工区':
# for production_id in production_ids:
# if lot_ids:
# lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if lot_id in lot_ids:
# workorder_id = production_id.workorder_ids.filtered(
# lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# else:
# workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'ready' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_start()
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in:
# 如果是最后一张外协入库单,则设置库存位置的预留数量 # 如果是最后一张外协入库单,则设置库存位置的预留数量
move_in = self.move_ids for production_id in production_ids:
if move_in: if lot_ids:
workorder = move_in.subcontract_workorder_id lot_id = production_id.move_raw_ids.move_line_ids.lot_id
workorders = workorder.production_id.workorder_ids # picking_ids = production_id.picking_ids.filtered(
subcontract_workorders = workorders.filtered( # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence') if lot_id in lot_ids:
# if workorder == subcontract_workorders[-1]: workorder_id = production_id.workorder_ids.filtered(
# self.env['stock.quant']._update_reserved_quantity( lambda a: a.state == 'progress' and a.is_subcontract)
# move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty, if not workorder_id:
# lot_id=move_in.move_line_ids.lot_id, continue
# package_id=False, owner_id=False, strict=False workorder_id.button_finish()
# ) else:
workorder.button_finish() workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'progress' and a.is_subcontract)
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id if not workorder_id:
if res and self.picking_type_id.id == picking_type_out: continue
move_out = self.move_ids workorder_id.button_finish()
if move_out: # lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
workorder = move_out.subcontract_workorder_id # picking_ids = workorder.production_id.picking_ids.filtered(
workorder.button_start() # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if move_in:
# workorder = move_in.subcontract_workorder_id
# workorders = workorder.production_id.workorder_ids
# subcontract_workorders = workorders.filtered(
# lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
# # if workorder == subcontract_workorders[-1]:
# # 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,
# # package_id=False, owner_id=False, strict=False
# # )
# workorder.button_finish()
if res and self.location_id.name == '制造前' and self.location_dest_id.name == '外协加工区':
for production_id in production_ids:
if lot_ids:
lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
if lot_id in lot_ids:
workorder_id = production_id.workorder_ids.filtered(
lambda a: a.state == 'progress' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_finish()
else:
workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'ready' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_start()
if self.location_id.name == '成品存货区' and self.location_dest_id.name == '客户': if self.location_id.name == '成品存货区' and self.location_dest_id.name == '客户':
sale_id = self.env['sale.order'].sudo().search( sale_id = self.env['sale.order'].sudo().search(
[('name', '=', self.origin)]) [('name', '=', self.origin)])
@@ -846,7 +817,6 @@ class ReStockMove(models.Model):
materiel_height = fields.Float(string='物料高度', digits=(16, 4)) materiel_height = fields.Float(string='物料高度', digits=(16, 4))
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True) part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True) part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
@api.depends('product_id') @api.depends('product_id')
def _compute_part_info(self): def _compute_part_info(self):
@@ -857,7 +827,7 @@ class ReStockMove(models.Model):
move.part_name = move.product_id.part_name move.part_name = move.product_id.part_name
elif move.product_id.categ_id.type == '坯料': elif move.product_id.categ_id.type == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d+)', move.product_id.name) match = re.search(r'(S\d{5}-\d)', move.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -889,7 +859,7 @@ class ReStockMove(models.Model):
continue continue
product_name = '' product_name = ''
logging.info('制造订单的产品 %s', production_id.product_id.name) logging.info('制造订单的产品 %s', production_id.product_id.name)
match = re.search(r'(S\d{5}-\d+)', production_id.product_id.name) match = re.search(r'(S\d{5}-\d)', production_id.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -967,7 +937,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': self.product_uom_qty, 'reserved_uom_qty': 1.0,
'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

@@ -383,7 +383,7 @@
<field name="process_parameters_id" <field name="process_parameters_id"
attrs="{'readonly': [('id', '!=', False),('routing_tag', '=', 'standard')]}" attrs="{'readonly': [('id', '!=', False),('routing_tag', '=', 'standard')]}"
string="参数" context="{'route_id':route_id,'production_id': production_id}" string="参数" context="{'route_id':route_id,'production_id': production_id}"
options="{'no_create': True}"/> options="{'no_create': True}" domain="[('routing_id', '=', 'route_id')]"/>
<field name="panel" readonly="1"/> <field name="panel" readonly="1"/>
<field name="routing_tag" readonly="1" widget="badge" <field name="routing_tag" readonly="1" widget="badge"
decoration-success="routing_tag == 'standard'" decoration-success="routing_tag == 'standard'"
@@ -602,7 +602,6 @@
<field name="part_number"/> <field name="part_number"/>
<field name="sale_order_id"/> <field name="sale_order_id"/>
<field name="deadline_of_delivery" icon="fa-calendar" enable_counters="1" filter_domain="[('deadline_of_delivery', 'ilike', self)]"/> <field name="deadline_of_delivery" icon="fa-calendar" enable_counters="1" filter_domain="[('deadline_of_delivery', 'ilike', self)]"/>
<field name="model_id"/>
</xpath> </xpath>
<xpath expr="//field[@name='product_variant_attributes']" position="attributes"> <xpath expr="//field[@name='product_variant_attributes']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>

View File

@@ -22,7 +22,7 @@
<field name="is_repeat"/> <field name="is_repeat"/>
<field name="reserved_duration"/> <field name="reserved_duration"/>
</field> </field>
<!-- <xpath expr="//notebook/page[1]" position="before"> <xpath expr="//notebook/page[1]" position="before">
<page string="可选工艺参数"> <page string="可选工艺参数">
<field name="optional_process_parameters"> <field name="optional_process_parameters">
<tree editable="bottom"> <tree editable="bottom">
@@ -30,9 +30,9 @@
<field name="is_delete_button" invisible="1"/> <field name="is_delete_button" invisible="1"/>
<field name="code" attrs="{'readonly': True}"/> <field name="code" attrs="{'readonly': True}"/>
<field name="name" required="1"/> <field name="name" required="1"/>
<field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/> --> <field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/>
<!-- 按钮列 --> <!-- 按钮列 -->
<!-- <button name="action_create_service_product" string="创建服务产品" type="object" <button name="action_create_service_product" string="创建服务产品" type="object"
class="btn-primary" class="btn-primary"
attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/> attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/>
<button name="action_hide_service_products" string="删除" type="object" <button name="action_hide_service_products" string="删除" type="object"
@@ -41,7 +41,7 @@
</tree> </tree>
</field> </field>
</page> </page>
</xpath> --> </xpath>
</field> </field>
</record> </record>
</data> </data>

View File

@@ -144,7 +144,7 @@
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/> statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
</xpath> </xpath>
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<!-- <button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button" <button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button"
icon="fa-credit-card" icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user" groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('pr_mp_count', '=', 0)]}"> attrs="{'invisible': [('pr_mp_count', '=', 0)]}">
@@ -154,7 +154,7 @@
</span> </span>
<span class="o_stat_text">采购申请</span> <span class="o_stat_text">采购申请</span>
</div> </div>
</button> --> </button>
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button" <button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
icon="fa-credit-card" icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user" groups="base.group_user,sf_base.group_sf_order_user"
@@ -677,9 +677,8 @@
<field name="inherit_id" ref="mrp.view_mrp_production_work_order_search"/> <field name="inherit_id" ref="mrp.view_mrp_production_work_order_search"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="product_id" position="after"> <field name="product_id" position="after">
<field name="part_number" string="零件图号"/> <field name="part_number" string="成品零件图号"/>
<field name="part_name" string="零件名称"/> <field name="model_id" string="模型id"/>
<field name="model_id" string="模型ID"/>
</field> </field>
<xpath expr="//filter[@name='progress']" position="after"> <xpath expr="//filter[@name='progress']" position="after">
<filter string="待检测" name="state" domain="[('state','=','to be detected')]"/> <filter string="待检测" name="state" domain="[('state','=','to be detected')]"/>

View File

@@ -12,18 +12,5 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="product_template_search_inherit_sf_manufacturing" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='categ_id']" position="after">
<field name="part_number" string="零件图号"/>
<field name="part_name" string="零件名称"/>
<field name="model_id" string="模型ID"/>
</xpath>
</field>
</record>
</data> </data>
</odoo> </odoo>

View File

@@ -73,7 +73,6 @@
<xpath expr="//field[@name='picking_type_id']" position="after"> <xpath expr="//field[@name='picking_type_id']" position="after">
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/> <field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
<field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/> <field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
<field name="model_id" string="模型ID" filter_domain="[('model_id', 'ilike', self)]"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -77,11 +77,11 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
if workorders[ if workorders[
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程': 0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorders[0].state = 'waiting' workorders[0].state = 'waiting'
# pr_ids = self.env['purchase.request'].sudo().search( pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.name), ('is_subcontract', '=', 'True'), ('state', '!=', 'rejected')]) [('origin', 'like', item.name), ('is_subcontract', '=', 'True'), ('state', '!=', 'rejected')])
# if not pr_ids: if not pr_ids:
# continue continue
# if not all(pr.state == 'draft' for pr in pr_ids): if not all(pr.state == 'draft' for pr in pr_ids):
# # 如果发现有记录的 state 不是 'draft',抛出异常 # 如果发现有记录的 state 不是 'draft',抛出异常
# raise UserError("有采购申请的状态不是 '草稿'") raise UserError("有采购申请的状态不是 '草稿'")
# pr_ids.state = 'rejected' pr_ids.state = 'rejected'

View File

@@ -24,7 +24,6 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res = {'status': 1, 'message': '成功'} res = {'status': 1, 'message': '成功'}
datas = request.httprequest.data datas = request.httprequest.data
model_id = None model_id = None
part_number = None
ret = json.loads(datas) ret = json.loads(datas)
ret = json.loads(ret['result']) ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret) logging.info('下发编程单:%s' % ret)
@@ -60,6 +59,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no']) res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
for production in productions: for production in productions:
model_id = production.product_id.model_id # 一个编程单的制造订单对应同一个模型
production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False}) production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False})
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
@@ -76,10 +76,6 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
cnc_workorder_has.write( cnc_workorder_has.write(
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret), {'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)}) 'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
# 一个编程单的制造订单对应同一个模型
model_id = productions[0].product_id.model_id
part_number = productions[0].product_id.part_number
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered( cnc_workorder = productions.workorder_ids.filtered(
@@ -91,25 +87,16 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel) program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
files_panel = os.listdir(program_path_tmp_panel) files_panel = os.listdir(program_path_tmp_panel)
panel_file_path = '' panel_file_path = ''
panel_file_name = ''
if files_panel: if files_panel:
for file in files_panel: for file in files_panel:
file_extension = os.path.splitext(file)[1] file_extension = os.path.splitext(file)[1]
if file_extension.lower() == '.pdf': if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file) panel_file_path = os.path.join(program_path_tmp_panel, file)
panel_file_name = os.path.splitext(file)[0]
logging.info('panel_file_path:%s' % panel_file_path) logging.info('panel_file_path:%s' % panel_file_path)
logging.info('panel_file_name:%s' % panel_file_name)
# 向编程单中添加二维码 # 向编程单中添加二维码
request.env['printing.utils'].add_qr_code_to_pdf( request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "模型ID%s" % model_id)
panel_file_path, cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
model_id,
"模型ID%s" % model_id,
"零件图号:%s" % part_number if part_number else None
)
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read()),
'cnc_worksheet_name': panel_file_name})
pre_workorder = productions.workorder_ids.filtered( pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework' lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
'cancel'] and ap.processing_panel == panel) 'cancel'] and ap.processing_panel == panel)

View File

@@ -1149,7 +1149,7 @@ class sfProductionProcessParameter(models.Model):
'processing_mm': item['processing_mm'], 'processing_mm': item['processing_mm'],
'gain_way':'外协', 'gain_way':'外协',
}) })
# production_process_parameter.create_service_product() production_process_parameter.create_service_product()
else: else:
production_process_parameter.gain_way = '外协' production_process_parameter.gain_way = '外协'
production_process_parameter.name = item['name'] production_process_parameter.name = item['name']
@@ -1161,9 +1161,9 @@ class sfProductionProcessParameter(models.Model):
[('materials_no', 'in', item['materials_model_ids_codes'])]) [('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.active = item['active'] production_process_parameter.active = item['active']
production_process_parameter.processing_mm = item['processing_mm'] production_process_parameter.processing_mm = item['processing_mm']
# if not production_process_parameter.outsourced_service_products: if not production_process_parameter.outsourced_service_products:
# production_process_parameter.create_service_product() production_process_parameter.create_service_product()
# production_process_parameter.create_work_center() production_process_parameter.create_work_center()
else: else:
raise ValidationError("表面工艺可选参数认证未通过") raise ValidationError("表面工艺可选参数认证未通过")

View File

@@ -15,7 +15,6 @@ class sf_production_plan(models.Model):
# _order = 'state desc, write_date desc' # _order = 'state desc, write_date desc'
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True) part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
state = fields.Selection([ state = fields.Selection([
('draft', '待排程'), ('draft', '待排程'),
('done', '已排程'), ('done', '已排程'),

View File

@@ -170,7 +170,6 @@
<field name="part_number"/> <field name="part_number"/>
<field name="order_deadline" filter_domain="[('order_deadline', 'ilike', self)]"/> <field name="order_deadline" filter_domain="[('order_deadline', 'ilike', self)]"/>
<field name="production_line_id"/> <field name="production_line_id"/>
<field name="model_id"/>
<filter string="待排程" name="draft" domain="[('state','=','draft')]"/> <filter string="待排程" name="draft" domain="[('state','=','draft')]"/>
<filter string="已排程" name="done" domain="[('state','=','done')]"/> <filter string="已排程" name="done" domain="[('state','=','done')]"/>
<filter string="加工中" name="processing" domain="[('state','=','processing')]"/> <filter string="加工中" name="processing" domain="[('state','=','processing')]"/>

View File

@@ -196,7 +196,8 @@ class ReSaleOrder(models.Model):
for order in self: for order in self:
order.purchase_order_count = len(order._get_purchase_orders().filtered( order.purchase_order_count = len(order._get_purchase_orders().filtered(
lambda po: po.purchase_type not in ['outsourcing'])) lambda po: po.purchase_type not in ['outsourcing']))
order.consignment_purchase_order_count = len(order._get_sale_to_purchase('outsourcing')) order.consignment_purchase_order_count = len(order._get_purchase_orders().filtered(
lambda po: po.purchase_type in ['outsourcing']))
def action_view_purchase_orders(self): def action_view_purchase_orders(self):
""" """
@@ -227,7 +228,8 @@ class ReSaleOrder(models.Model):
委外加工 委外加工
""" """
self.ensure_one() self.ensure_one()
outsourcing_purchase_order_ids = self._get_sale_to_purchase('outsourcing') outsourcing_purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type in ['outsourcing']).ids
action = { action = {
'res_model': 'purchase.order', 'res_model': 'purchase.order',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
@@ -245,14 +247,6 @@ class ReSaleOrder(models.Model):
}) })
return action return action
def _get_sale_to_purchase(self, purchase_type):
"""查询满足条件的采购订单"""
purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type == purchase_type).ids
order_ids = self.env['purchase.order'].sudo().search(
[('origin', '=', self.name), ('purchase_type', '=', purchase_type)]).ids
return list(set(purchase_order_ids) | set(order_ids))
class ResaleOrderLine(models.Model): class ResaleOrderLine(models.Model):
_inherit = 'sale.order.line' _inherit = 'sale.order.line'
@@ -352,10 +346,10 @@ class RePurchaseOrder(models.Model):
if purchase.order_line[0].product_id.categ_id.name == '坯料': if purchase.order_line[0].product_id.categ_id.name == '坯料':
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协': if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
purchase.purchase_type = 'outsourcing' purchase.purchase_type = 'outsourcing'
# request_lines = self.order_line.mapped('purchase_request_lines') request_lines = self.order_line.mapped('purchase_request_lines')
# # 检查是否存在 is_subcontract 为 True 的行 # 检查是否存在 is_subcontract 为 True 的行
# if any(line.is_subcontract for line in request_lines): if any(line.is_subcontract for line in request_lines):
# purchase.purchase_type = 'consignment' purchase.purchase_type = 'consignment'
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '预警'), ('overdue', '已逾期')], delivery_warning = fields.Selection([('normal', '正常'), ('warning', '预警'), ('overdue', '已逾期')],
@@ -390,28 +384,28 @@ class RePurchaseOrder(models.Model):
if not line.taxes_id: if not line.taxes_id:
raise UserError('请对【产品】中的【税】进行选择') raise UserError('请对【产品】中的【税】进行选择')
# def get_purchase_request(self, consecutive_process_parameters, production): def get_purchase_request(self, consecutive_process_parameters, production):
# result = [] result = []
# for pp in consecutive_process_parameters: for pp in consecutive_process_parameters:
# server_template = self.env['product.template'].search( server_template = self.env['product.template'].search(
# [('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id), [('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id),
# ('detailed_type', '=', 'service')]) ('detailed_type', '=', 'service')])
# # route_ids # route_ids
# result.append({ result.append({
# "product_id": server_template.product_variant_id.id, "product_id": server_template.product_variant_id.id,
# 'related_product': production.product_id.id, 'related_product': production.product_id.id,
# "name": production.procurement_group_id.name, "name": production.procurement_group_id.name,
# "date_required": fields.Datetime.now(), "date_required": fields.Datetime.now(),
# "product_uom_id":server_template.uom_id.id, "product_uom_id":server_template.uom_id.id,
# "product_qty": production.product_qty, "product_qty": production.product_qty,
# "request_id": False, "request_id": False,
# "move_dest_ids": False, "move_dest_ids": False,
# "orderpoint_id": False, "orderpoint_id": False,
# 'is_subcontract':True, 'is_subcontract':True,
# 'group_id':production.procurement_group_id.id, 'group_id':production.procurement_group_id.id,
# 'production_name':pp.production_id.name, 'production_name':pp.production_id.name,
# }) })
# return result return result
def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names): def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names):
for pp in consecutive_process_parameters: for pp in consecutive_process_parameters: