Merge branch 'feature/commercially_launched' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/commercially_launched

This commit is contained in:
mgw
2025-03-28 14:03:21 +08:00
17 changed files with 167 additions and 39 deletions

View File

@@ -1,4 +1,5 @@
import re import re
import ast
from odoo import models, fields, api from odoo import models, fields, api
@@ -20,6 +21,13 @@ class PurchaseRequest(models.Model):
if pr.state != 'draft' and pr.rule_new_add: if pr.state != 'draft' and pr.rule_new_add:
pr.rule_new_add = False pr.rule_new_add = False
def action_view_purchase_order(self):
action = super(PurchaseRequest, self).action_view_purchase_order()
origin_context = ast.literal_eval(action['context'])
if 'search_default_draft' in origin_context:
origin_context.pop('search_default_draft')
action['context'] = origin_context
return action
class PurchaseRequestLine(models.Model): class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line' _inherit = 'purchase.request.line'

View File

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

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
'name': "机企猫 采购审批流程",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "https://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['purchase_request_tier_validation'],
# always loaded
'data': [
],
}

View File

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

View File

@@ -0,0 +1,24 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class PurchaseRequest(models.Model):
_inherit = 'purchase.request'
def _validate_tier(self, tiers=False):
res = super(PurchaseRequest, self)._validate_tier(tiers)
# 检查是否所有审批都已通过
all_approved = all(
tier_review.status == 'approved'
for tier_review in self.review_ids
)
if self.review_ids and all_approved: # 确保有审批记录
self.state = 'approved'
return res

View File

@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
{ {
'name': "机企猫 采购审批流程", 'name': "机企猫 采购申请审批流程",
'summary': """ 'summary': """
Short (1 phrase/line) summary of the module's purpose, used as 采购申请审批流程""",
subtitle on modules listing or apps.openerp.com""",
'description': """ 'description': """
Long description of module's purpose 采购申请审批流程""",
""",
'author': "My Company", 'author': "My Company",
'website': "https://www.yourcompany.com", 'website': "https://www.yourcompany.com",

View File

@@ -421,3 +421,4 @@ class EmbryoRedundancy(models.Model):
width = fields.Float('宽度(mm)', required=True) width = fields.Float('宽度(mm)', required=True)
height = fields.Float('高度(mm)', required=True) height = fields.Float('高度(mm)', required=True)
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)
remark = fields.Char('描述')

View File

@@ -645,6 +645,7 @@
<field name="long"/> <field name="long"/>
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="remark"/>
</tree> </tree>
</field> </field>
</record> </record>

View File

@@ -5,6 +5,8 @@ import logging
import json import json
import os import os
import re import re
import traceback
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
@@ -257,14 +259,46 @@ class MrpProduction(models.Model):
], string='工序状态', default='待装夹') ], string='工序状态', default='待装夹')
# 零件图号 # 零件图号
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) part_number = fields.Char('零件图号', compute='_compute_part_info', store=True)
# 上传零件图纸 # 上传零件图纸
part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True) part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True)
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True) quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True) part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
@api.depends('product_id')
def _compute_part_info(self):
try:
for production_id in self:
if production_id.product_id.categ_id.type == '成品':
production_id.part_number = production_id.product_id.part_number
production_id.part_name = production_id.product_id.part_name
elif production_id.product_id.categ_id.type == '坯料':
product_name = ''
match = re.search(r'(S\d{5}-\d)', production_id.product_id.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
if production_id.sale_order_id:
sale_order = production_id.sale_order_id
else:
sale_order_name = ''
match = re.search(r'(S\d+)', production_id.product_id.name)
if match:
sale_order_name = match.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
logging.info("product_name is :%s" % product_name)
filtered_order_line = sale_order.order_line.filtered(
lambda production: re.search(f'{product_name}$', production.product_id.name)
)
if filtered_order_line:
production_id.part_number = filtered_order_line.part_number
production_id.part_name = filtered_order_line.part_name
except Exception as e:
traceback_error = traceback.format_exc()
logging.error("制造订单零件图号 零件名称获取失败:%s" % traceback_error)
# 判断制造的产品类型 # 判断制造的产品类型
production_product_type = fields.Selection([ production_product_type = fields.Selection([

View File

@@ -1533,7 +1533,7 @@ class ResMrpWorkOrder(models.Model):
# workorder.rfid_code_old = rfid_code # workorder.rfid_code_old = rfid_code
# workorder.rfid_code = False # workorder.rfid_code = False
logging.info('workorder.rfid_code:%s' % workorder.rfid_code) logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
# if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺', '切割']:
if is_production_id is True: if is_production_id is True:
logging.info('product_qty:%s' % record.production_id.product_qty) logging.info('product_qty:%s' % record.production_id.product_qty)
for move_raw_id in record.production_id.move_raw_ids: for move_raw_id in record.production_id.move_raw_ids:
@@ -1548,6 +1548,17 @@ class ResMrpWorkOrder(models.Model):
# if raw_move: # if raw_move:
# raw_move.write({'state': 'done'}) # raw_move.write({'state': 'done'})
if record.production_id.state != 'rework': if record.production_id.state != 'rework':
# 如果工单包含了外协工序,需要预留数量
if self.move_raw_ids.move_orig_ids.subcontract_workorder_id:
location_id = self.move_raw_ids.location_id
quant = self.move_raw_ids.lot_ids.quant_ids.filtered(lambda q: q.location_id.id == location_id.id)
if quant.reserved_quantity == 0:
self.env['stock.quant']._update_reserved_quantity(
self.move_raw_ids.product_id,
location_id,
quant.quantity,
lot_id=quant.lot_id,
)
record.production_id.button_mark_done1() record.production_id.button_mark_done1()
# record.production_id.state = 'done' # record.production_id.state = 'done'

View File

@@ -174,7 +174,6 @@ class ResProductMo(models.Model):
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False) name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
@api.constrains('seller_ids') @api.constrains('seller_ids')
def _check_seller_ids(self): def _check_seller_ids(self):
if self.categ_type == '表面工艺': if self.categ_type == '表面工艺':
@@ -896,8 +895,9 @@ class ResProductMo(models.Model):
'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long), 'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long),
'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width), 'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height), 'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
'model_volume': self.format_float(item['blank_volume']), 'model_volume': self.format_float((item['model_long'] + embryo_redundancy_id.long) * (
'model_area': self.format_float(item['blank_area']), item['model_width'] + embryo_redundancy_id.width) * (
item['model_height'] + embryo_redundancy_id.height)),
'product_model_type_id': model_type.id, 'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'], 'model_processing_panel': item['processing_panel_detail'],
'model_machining_precision': item['model_machining_precision'], 'model_machining_precision': item['model_machining_precision'],

View File

@@ -59,18 +59,15 @@ 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 button_confirm(self): def button_confirm(self):
# super().button_confirm() for record in self:
# workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')]) for line in record.order_line:
# for workorder in workorders: if line.product_qty <= 0:
# if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True: raise UserError('请对【产品】中的【数量】进行输入')
# move_out = workorder.move_subcontract_workorder_ids[1] if line.price_unit <= 0:
# for mo in move_out: raise UserError('请对【产品】中的【单价】进行输入')
# if mo.state != 'done': return super(PurchaseOrder, self).button_confirm()
# mo.write({'state': 'assigned', 'production_id': False})
# if not mo.move_line_ids:
# self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
# return True
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id') origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True, origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True,

View File

@@ -564,6 +564,18 @@ class StockPicking(models.Model):
sale_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True) sale_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code') picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
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)
@api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name')
def _compute_part_info(self):
for picking in self:
# 聚合所有关联行的 part_number 和 part_name
part_numbers = picking.move_ids_without_package.mapped('part_number')
part_names = picking.move_ids_without_package.mapped('part_name')
picking.part_numbers = ','.join(filter(None, part_numbers))
picking.part_names = ','.join(filter(None, part_names))
@api.depends('move_ids', 'move_ids.product_id') @api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self): def _compute_move_ids(self):
for item in self: for item in self:
@@ -678,7 +690,8 @@ class StockPicking(models.Model):
# 如果当前工单是是制造订单的最后一个工艺外协工单 # 如果当前工单是是制造订单的最后一个工艺外协工单
if workorder == next((workorder for workorder in reversed(sorted_workorders) if workorder.is_subcontract), if workorder == next((workorder for workorder in reversed(sorted_workorders) if workorder.is_subcontract),
None): None):
move_dest_id = item.move_raw_ids[0].id if item.move_raw_ids:
move_dest_id = item.move_raw_ids[0].id
else: else:
# 从sorted_workorders中找到上一工单的move # 从sorted_workorders中找到上一工单的move
if len(sorted_workorders) > 1: if len(sorted_workorders) > 1:
@@ -714,6 +727,7 @@ class StockPicking(models.Model):
moves_out._action_confirm() moves_out._action_confirm()
moves_out._assign_picking_post_process(new=new_picking) moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id') @api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self): def _compute_state(self):
super(StockPicking, self)._compute_state() super(StockPicking, self)._compute_state()
@@ -837,6 +851,7 @@ class ReStockMove(models.Model):
# 'route_ids': False if not route else [(4, route.id)], # 'route_ids': False if not route else [(4, route.id)],
'date_deadline': datetime.now(), 'date_deadline': datetime.now(),
'picking_type_id': picking_type_id, 'picking_type_id': picking_type_id,
# 'is_subcontract': True,
} }
return move_values return move_values
@@ -1107,6 +1122,13 @@ class ReStockMove(models.Model):
self.state = 'assigned' self.state = 'assigned'
return self.action_show_details() return self.action_show_details()
def _prepare_move_line_vals(self, quantity=None, reserved_quant=None):
res = super(ReStockMove, self)._prepare_move_line_vals(quantity, reserved_quant)
if self.subcontract_workorder_id:
if self.subcontract_workorder_id.production_id.move_raw_ids.move_line_ids:
res['lot_id'] = self.subcontract_workorder_id.production_id.move_raw_ids.move_line_ids[0].lot_id.id
return res
class ReStockQuant(models.Model): class ReStockQuant(models.Model):
_inherit = 'stock.quant' _inherit = 'stock.quant'

View File

@@ -18,7 +18,7 @@
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before"> <xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" /> <field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='glb_url']" position="before"> <xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="after">
<field name="part_number" optional="show" class="section_and_note_text"/> <field name="part_number" optional="show" class="section_and_note_text"/>
</xpath> </xpath>
<!-- <xpath expr="//field[@name='order_line']/tree/field[@name='remark']" position="before"> --> <!-- <xpath expr="//field[@name='order_line']/tree/field[@name='remark']" position="before"> -->

View File

@@ -68,14 +68,8 @@
context="{'group_by': 'retrospect_ref'}"/> context="{'group_by': 'retrospect_ref'}"/>
</xpath> </xpath>
<xpath expr="//field[@name='picking_type_id']" position="after"> <xpath expr="//field[@name='picking_type_id']" position="after">
<field name="product_id" <field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
string="零件图号" <field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
filter_domain="[('product_id.part_number', 'ilike', self)]"
/>
<field name="product_id"
string="零件名称"
filter_domain="[('product_id.part_name', 'ilike', self)]"
/>
</xpath> </xpath>
</field> </field>
</record> </record>
@@ -97,7 +91,8 @@
<attribute name="invisible">True</attribute> <attribute name="invisible">True</attribute>
</xpath> </xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after"> <xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after">
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k" title="Assign Serial Numbers"> <button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k"
title="Assign Serial Numbers">
<span>更新序列号</span> <span>更新序列号</span>
</button> </button>
</xpath> </xpath>

View File

@@ -191,7 +191,8 @@ class SFMessageWork(models.Model):
def write(self, vals): def write(self, vals):
res = super(SFMessageWork, self).write(vals) res = super(SFMessageWork, self).write(vals)
if ('leave_id' in vals and vals['leave_id'] is False or 'date_planned_start' in vals and vals['date_planned_start'] is False) \ for record in self:
and self.schedule_state != '未排': if ('leave_id' in vals and vals['leave_id'] is False or 'date_planned_start' in vals and vals['date_planned_start'] is False) \
self.add_queue('计划数据异常跟踪') and record.schedule_state != '未排':
record.add_queue('计划数据异常跟踪')
return res return res

View File

@@ -3214,6 +3214,7 @@ class EmbryoRedundancySync(models.Model):
embryo_redundancy.width = item['width'] embryo_redundancy.width = item['width']
embryo_redundancy.height = item['height'] embryo_redundancy.height = item['height']
embryo_redundancy.active = item['active'] embryo_redundancy.active = item['active']
embryo_redundancy.remark = item['remark']
else: else:
self.env['sf.embryo.redundancy'].sudo().create({ self.env['sf.embryo.redundancy'].sudo().create({
"name": item['name'], "name": item['name'],
@@ -3222,4 +3223,5 @@ class EmbryoRedundancySync(models.Model):
"width": item['width'], "width": item['width'],
"height": item['height'], "height": item['height'],
"active": item['active'], "active": item['active'],
"remark": item['remark'],
}) })