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 ast
from odoo import models, fields, api
@@ -20,6 +21,13 @@ class PurchaseRequest(models.Model):
if pr.state != 'draft' and pr.rule_new_add:
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):
_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 -*-
{
'name': "机企猫 采购审批流程",
'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",

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import logging
import json
import os
import re
import traceback
import requests
from itertools import groupby
from collections import defaultdict, namedtuple
@@ -257,14 +259,46 @@ class MrpProduction(models.Model):
], 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)
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([

View File

@@ -1533,7 +1533,7 @@ class ResMrpWorkOrder(models.Model):
# workorder.rfid_code_old = rfid_code
# workorder.rfid_code = False
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:
logging.info('product_qty:%s' % record.production_id.product_qty)
for move_raw_id in record.production_id.move_raw_ids:
@@ -1548,6 +1548,17 @@ class ResMrpWorkOrder(models.Model):
# if raw_move:
# raw_move.write({'state': 'done'})
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.state = 'done'

View File

@@ -173,7 +173,6 @@ class ResProductMo(models.Model):
'压紧方式', domain=[('type', '=', '压紧方式')])
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
@api.constrains('seller_ids')
def _check_seller_ids(self):
@@ -896,8 +895,9 @@ class ResProductMo(models.Model):
'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_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
'model_volume': self.format_float(item['blank_volume']),
'model_area': self.format_float(item['blank_area']),
'model_volume': self.format_float((item['model_long'] + embryo_redundancy_id.long) * (
item['model_width'] + embryo_redundancy_id.width) * (
item['model_height'] + embryo_redundancy_id.height)),
'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'],
'model_machining_precision': item['model_machining_precision'],
@@ -1114,9 +1114,9 @@ class ResProductMo(models.Model):
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
return base64_data
# 增加产品表面积
class ResProductFixture(models.Model):

View File

@@ -59,18 +59,15 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id)
# def button_confirm(self):
# super().button_confirm()
# workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')])
# for workorder in workorders:
# if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
# move_out = workorder.move_subcontract_workorder_ids[1]
# for mo in move_out:
# if mo.state != 'done':
# mo.write({'state': 'assigned', 'production_id': False})
# if not mo.move_line_ids:
# self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
# return True
def button_confirm(self):
for record in self:
for line in record.order_line:
if line.product_qty <= 0:
raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入')
return super(PurchaseOrder, self).button_confirm()
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,

View File

@@ -564,6 +564,18 @@ class StockPicking(models.Model):
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')
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')
def _compute_move_ids(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),
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:
# 从sorted_workorders中找到上一工单的move
if len(sorted_workorders) > 1:
@@ -714,6 +727,7 @@ class StockPicking(models.Model):
moves_out._action_confirm()
moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self):
super(StockPicking, self)._compute_state()
@@ -837,6 +851,7 @@ class ReStockMove(models.Model):
# 'route_ids': False if not route else [(4, route.id)],
'date_deadline': datetime.now(),
'picking_type_id': picking_type_id,
# 'is_subcontract': True,
}
return move_values
@@ -1106,6 +1121,13 @@ class ReStockMove(models.Model):
if self.state != 'assigned':
self.state = 'assigned'
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):

View File

@@ -18,7 +18,7 @@
<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')]}" />
</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"/>
</xpath>
<!-- <xpath expr="//field[@name='order_line']/tree/field[@name='remark']" position="before"> -->

View File

@@ -68,14 +68,8 @@
context="{'group_by': 'retrospect_ref'}"/>
</xpath>
<xpath expr="//field[@name='picking_type_id']" position="after">
<field name="product_id"
string="零件图号"
filter_domain="[('product_id.part_number', 'ilike', self)]"
/>
<field name="product_id"
string="零件名称"
filter_domain="[('product_id.part_name', 'ilike', self)]"
/>
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
<field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
</xpath>
</field>
</record>
@@ -97,7 +91,8 @@
<attribute name="invisible">True</attribute>
</xpath>
<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>
</button>
</xpath>

View File

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

View File

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