Compare commits

..

2 Commits

Author SHA1 Message Date
胡尧
307e860fe0 增加提示模块 2025-06-20 15:16:43 +08:00
胡尧
bd27f288f7 增加提示模块 2025-06-20 15:16:25 +08:00
43 changed files with 415 additions and 1910 deletions

View File

@@ -190,7 +190,7 @@ def _create(self, data_list):
# 如果该用户组被限制创建或更新操作 # 如果该用户组被限制创建或更新操作
if rec['is_create_or_update']: if rec['is_create_or_update']:
raise UserError( raise UserError(
_("您没有执行此操作的权限。请联系管理员")) _("您没有执行此操作%s的权限。请联系管理员" % group_xml_id))
else: else:
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑 # 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑 # 例如,记录日志、发送通知或者简单地跳过这部分逻辑

View File

@@ -10,7 +10,6 @@
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'views/sale_order_view.xml', 'views/sale_order_view.xml',
'views/purchase_order.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',

View File

@@ -16,69 +16,6 @@ class PurchaseOrder(models.Model):
('rejected', '已驳回') ('rejected', '已驳回')
], string='Status', readonly=True, index=True, copy=False, default='draft', tracking=True) ], string='Status', readonly=True, index=True, copy=False, default='draft', tracking=True)
# 成品采购订单对应的坯料采购申请单和采购订单数量
purchase_request_count = fields.Integer('子·采购申请数量', compute='_compute_purchase_request')
purchase_order_count = fields.Integer('子·采购订单数量', compute='_compute_purchase_request')
@api.depends('state')
def _compute_purchase_request(self):
for record in self:
purchase_request_ids, purchase_order_ids = record.get_purchase_request_order()
record.purchase_request_count = len(purchase_request_ids)
record.purchase_order_count = len(purchase_order_ids)
def action_view_preform_body_purchase_request(self):
self.ensure_one()
name_list = self._get_pinking_name()
purchase_request_ids = self.env['purchase.request'].search([('origin', 'in', name_list)])
action = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',
}
if len(purchase_request_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_request_ids[0].id,
})
else:
action.update({
'name': _("子·采购申请"),
'domain': [('id', 'in', purchase_request_ids.ids)],
'view_mode': 'tree,form',
})
return action
def action_view_preform_body_purchase_order(self):
self.ensure_one()
name_list = self._get_pinking_name()
purchase_order_ids = self.env['purchase.order'].search([('origin', 'in', name_list)])
action = {
'res_model': 'purchase.order',
'type': 'ir.actions.act_window',
}
if len(purchase_order_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_order_ids[0].id,
})
else:
action.update({
'name': _("子·采购订单"),
'domain': [('id', 'in', purchase_order_ids.ids)],
'view_mode': 'tree,form',
})
return action
def get_purchase_request_order(self):
name_list = self._get_pinking_name()
purchase_request_ids = self.env['purchase.request'].search([('origin', 'in', name_list)])
purchase_order_ids = self.env['purchase.order'].search([('origin', 'in', name_list)])
return purchase_request_ids, purchase_order_ids
def _get_pinking_name(self):
return [picking_id.name for picking_id in self.picking_ids if picking_id.name]
def button_confirm(self): def button_confirm(self):
res = super(PurchaseOrder, self).button_confirm() res = super(PurchaseOrder, self).button_confirm()

View File

@@ -16,7 +16,6 @@ class PurchaseRequest(models.Model):
) )
rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True) rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True)
rule_purchase_to_request = fields.Boolean('采购单根据规则创建坯料采购申请', default=False)
@api.depends('state') @api.depends('state')
def _compute_state(self): def _compute_state(self):

View File

@@ -45,14 +45,3 @@ class StockPicking(models.Model):
(4, x.id) for x in backorder_ids.move_ids if x.product_id.id in purchase_request_lines.mapped('product_id.id') (4, x.id) for x in backorder_ids.move_ids if x.product_id.id in purchase_request_lines.mapped('product_id.id')
] ]
return res return res
def _subcontracted_produce(self, subcontract_details):
super()._subcontracted_produce(subcontract_details)
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
if self:
pr_ids = self.env["purchase.request"].sudo().search(
[('origin', 'like', self.name), ('rule_purchase_to_request', '=', True), ('state', '=', 'draft')])
if pr_ids:
pr_ids.write({'need_validation': False})
pr_ids.write({"state": "approved", 'need_validation': True, 'rule_new_add': False})

View File

@@ -26,7 +26,7 @@ class StockRule(models.Model):
request_data = rule._prepare_purchase_request( request_data = rule._prepare_purchase_request(
procurement.origin, procurement.values procurement.origin, procurement.values
) )
request_data = self._update_request_data(procurement, request_data) request_data.update({'rule_new_add': True})
pr = purchase_request_model.create(request_data) pr = purchase_request_model.create(request_data)
cache[domain] = pr cache[domain] = pr
elif ( elif (
@@ -44,18 +44,6 @@ class StockRule(models.Model):
request_line_data.update({'origin': procurement.origin}) request_line_data.update({'origin': procurement.origin})
purchase_request_line_model.create(request_line_data) purchase_request_line_model.create(request_line_data)
def _update_request_data(self, procurement, request_data):
sp = self.env['stock.picking'].sudo().search([('name', '=', procurement.origin)])
if len(sp) == 1:
po = self.env['purchase.order'].sudo().search(
[('name', '=', sp.origin), ('purchase_type', '=', 'outsourcing')])
if po:
request_data.update({'rule_purchase_to_request': True})
else:
request_data.update({'rule_new_add': True})
return request_data
def _run_buy(self, procurements): def _run_buy(self, procurements):
# 如果补货组相同,并且产品相同,则合并 # 如果补货组相同,并且产品相同,则合并
procurements_dict = defaultdict() procurements_dict = defaultdict()

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="purchase_order_form_jikimo_purchase_request" model="ir.ui.view">
<field name="name">purchase.order.inherited.form.jikimo.purchase.request</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="mrp_subcontracting_purchase.purchase_order_form_mrp_subcontracting_purchase"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('oe_button_box')]/button[@name='action_view_subcontracting_resupply']" position="before">
<button
class="oe_stat_button" name="action_view_preform_body_purchase_order"
type="object" icon="fa-truck" attrs="{'invisible': [('purchase_order_count', '=', 0)]}" groups="stock.group_stock_user">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="purchase_order_count"/></span>
<span class="o_stat_text">子·采购订单</span>
</div>
</button>
<button
class="oe_stat_button" name="action_view_preform_body_purchase_request"
type="object" icon="fa-truck" attrs="{'invisible': [('purchase_request_count', '=', 0)]}" groups="stock.group_stock_user">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="purchase_request_count"/></span>
<span class="o_stat_text">子·采购申请</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -67,16 +67,6 @@
<field name="part_number"/> <field name="part_number"/>
<field name="part_name" invisible="1"/> <field name="part_name" invisible="1"/>
</xpath> </xpath>
<xpath expr="//tree" position="inside">
<header>
<button
name="%(purchase_request.action_purchase_request_line_make_purchase_order)d"
string="创建询价单"
type="action"
class="btn-primary"
/>
</header>
</xpath>
</field> </field>
</record> </record>

View File

@@ -13,18 +13,8 @@
'depends': ['sf_plan'], 'depends': ['sf_plan'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'data/stock_route_group.xml',
<<<<<<< HEAD
'views/sale_order_views.xml',
'views/demand_plan.xml', 'views/demand_plan.xml',
'views/demand_main_plan.xml',
=======
'views/demand_plan.xml',
>>>>>>> develop
'views/stock_route.xml',
'wizard/sf_demand_plan_print_wizard_view.xml', 'wizard/sf_demand_plan_print_wizard_view.xml',
'wizard/sf_release_plan_wizard_views.xml',
'views/menu_view.xml',
], ],
'demo': [ 'demo': [
], ],

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="0">
<record id="stock_route_group_automation_sf" model="stock.route.group">
<field name="name">自动化产线加工</field>
<field name="code">automation</field>
</record>
<record id="stock_route_group_manual_sf" model="stock.route.group">
<field name="name">人工线下加工</field>
<field name="code">manual</field>
</record>
<record id="stock_route_group_purchase_sf" model="stock.route.group">
<field name="name">外购</field>
<field name="code">purchase</field>
</record>
<record id="stock_route_group_outsourcing_sf" model="stock.route.group">
<field name="name">委外加工</field>
<field name="code">outsourcing</field>
</record>
</data>
</odoo>

View File

@@ -1,25 +0,0 @@
# migrations/1.1.0/post-migrate.py
import os
import csv
import logging
from odoo import api, SUPERUSER_ID
_logger = logging.getLogger(__name__)
#需求计划模块升级的时候,同步处理存量需求明细数据,关联上新加的需求计划主表
def migrate(cr, version):
# 获取环境
env = api.Environment(cr, SUPERUSER_ID, {})
ProductionLine = env['sf.production.demand.plan']
DemandPlan = env['sf.demand.main.plan']
lines = ProductionLine.search([('demand_plan_id', '=', False)])
for line in lines:
vals = {
'sale_order_id': line.sale_order_id.id,
'sale_order_line_id': line.sale_order_line_id.id,
'line_ids': line.ids
}
new_plan = DemandPlan.create(vals)
line.write({'demand_plan_id': new_plan.id})

View File

@@ -1,11 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import mrp_bom
from . import mrp_production
from . import sf_demand_main_plan
from . import sf_production_demand_plan from . import sf_production_demand_plan
from . import sale_order from . import sale_order
from . import stock_route
from . import stock_rule
from . import purchase_request
from . import purchase_order

View File

@@ -1,20 +0,0 @@
from odoo import models, fields
class MrpBom(models.Model):
_inherit = 'mrp.bom'
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
def bom_create(self, product, bom_type, product_type, code=None):
bom_id = self.env['mrp.bom'].create({
'product_tmpl_id': product.product_tmpl_id.id,
'type': bom_type,
# 'subcontractor_id': '' or subcontract.partner_id.id,
'product_qty': 1,
'product_uom_id': 1,
'code': code
})
if bom_type == 'subcontract' and product_type is not False:
subcontract = self.get_supplier(product.materials_type_id)
bom_id.subcontractor_id = subcontract.partner_id.id
return bom_id

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api
class MrpProduction(models.Model):
_inherit = 'mrp.production'
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.depends('demand_plan_line_id')
def _compute_production_type(self):
for production in self:
if production.demand_plan_line_id.supply_method == 'automation':
production.production_type = '自动化产线加工'
elif production.demand_plan_line_id.supply_method == 'manual':
production.production_type = '人工线下加工'
else:
production.production_type = None
def _get_purchase_request(self):
"""获取跟制造订单相关的采购申请单"""
pr_ids = self.env['purchase.request'].sudo().search([('origin', '=', self.name)])
return pr_ids

View File

@@ -1,22 +0,0 @@
from odoo import api, fields, models, _
from odoo.tools import float_compare
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.depends('origin', 'demand_plan_line_id')
def _compute_purchase_type(self):
for purchase in self:
if purchase.order_line[0].product_id.categ_id.name == '坯料':
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
purchase.purchase_type = 'outsourcing'
else:
if purchase.demand_plan_line_id.supply_method == 'outsourcing':
purchase.purchase_type = 'outsourcing'
elif purchase.demand_plan_line_id.supply_method == 'purchase':
purchase.purchase_type = 'outside'

View File

@@ -1,110 +0,0 @@
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line'
_description = '采购申请明细'
supply_method = fields.Selection([
('automation', "自动化产线加工"),
('manual', "人工线下加工"),
('purchase', "外购"),
('outsourcing', "委外加工"),
], string='供货方式', readonly=True)
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.depends('demand_plan_line_id')
def _compute_supply_method(self):
for prl in self:
if prl.demand_plan_line_id:
prl.supply_method = prl.demand_plan_line_id.supply_method
else:
prl.supply_method = None
class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
_inherit = "purchase.request.line.make.purchase.order"
def make_purchase_order(self):
res = []
purchase_obj = self.env["purchase.order"]
po_line_obj = self.env["purchase.order.line"]
purchase = False
if len(set([item_id.line_id.supply_method for item_id in self.item_ids])) > 1:
raise ValidationError('不同供货方式不可合并创建询价单!')
for item in self.item_ids:
line = item.line_id
if item.product_qty <= 0.0:
raise UserError(_("Enter a positive quantity."))
if self.purchase_order_id:
purchase = self.purchase_order_id
if not purchase:
po_data = self._prepare_purchase_order(
line.request_id.picking_type_id,
line.request_id.group_id,
line.company_id,
line.request_id.origin,
)
po_data['demand_plan_line_id'] = item.line_id.demand_plan_line_id.id
# po_data.update({'related_product':line.related_product.id})
purchase = purchase_obj.create(po_data)
# Look for any other PO line in the selected PO with same
# product and UoM to sum quantities instead of creating a new
# po line
domain = self._get_order_line_search_domain(purchase, item)
available_po_lines = po_line_obj.search(domain)
new_pr_line = True
# If Unit of Measure is not set, update from wizard.
if not line.product_uom_id:
line.product_uom_id = item.product_uom_id
# Allocation UoM has to be the same as PR line UoM
alloc_uom = line.product_uom_id
wizard_uom = item.product_uom_id
if available_po_lines and not item.keep_description:
new_pr_line = False
po_line = available_po_lines[0]
po_line.purchase_request_lines = [(4, line.id)]
po_line.move_dest_ids |= line.move_dest_ids
po_line_product_uom_qty = po_line.product_uom._compute_quantity(
po_line.product_uom_qty, alloc_uom
)
wizard_product_uom_qty = wizard_uom._compute_quantity(
item.product_qty, alloc_uom
)
all_qty = min(po_line_product_uom_qty, wizard_product_uom_qty)
self.create_allocation(po_line, line, all_qty, alloc_uom)
else:
po_line_data = self._prepare_purchase_order_line(purchase, item)
if item.keep_description:
po_line_data["name"] = item.name
if line.related_product:
po_line_data.update({'related_product': line.related_product.id})
po_line = po_line_obj.create(po_line_data)
po_line_product_uom_qty = po_line.product_uom._compute_quantity(
po_line.product_uom_qty, alloc_uom
)
wizard_product_uom_qty = wizard_uom._compute_quantity(
item.product_qty, alloc_uom
)
all_qty = min(po_line_product_uom_qty, wizard_product_uom_qty)
self.create_allocation(po_line, line, all_qty, alloc_uom)
self._post_process_po_line(item, po_line, new_pr_line)
res.append(purchase.id)
purchase_requests = self.item_ids.mapped("request_id")
purchase_requests.button_in_progress()
return {
"domain": [("id", "in", res)],
"name": _("RFQ"),
"view_mode": "tree,form",
"res_model": "purchase.order",
"view_id": False,
"context": False,
"type": "ir.actions.act_window",
}

View File

@@ -9,53 +9,14 @@ class ReSaleOrder(models.Model):
compute='_compute_mrp_production_ids', compute='_compute_mrp_production_ids',
string='与此销售订单相关联的制造订单', string='与此销售订单相关联的制造订单',
groups='mrp.group_mrp_user', store=True) groups='mrp.group_mrp_user', store=True)
demand_plan_ids = fields.Many2many(comodel_name="sf.demand.main.plan",
string="需求计划", readonly=True)
demand_plan_count = fields.Integer(
"需求计划生成计数",
compute='_compute_demand_plan_count',
)
#暂时不知道哪里用到了
@api.depends('demand_plan_ids.line_ids.status')
def _compute_purchase_request_count(self):
for record in self:
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', record.name)])
if pr_ids:
record.purchase_request_purchase_order_count = len(pr_ids)
else:
record.purchase_request_purchase_order_count = 0
#计算需求计划生成计数
@api.depends('demand_plan_ids.line_ids')
def _compute_demand_plan_count(self):
for line in self:
demand_plan = self.env['sf.production.demand.plan'].sudo().search([('sale_order_id', '=', line.id)])
line.demand_plan_count = len(demand_plan)
def sale_order_create_line(self, product, item): def sale_order_create_line(self, product, item):
ret = super(ReSaleOrder, self).sale_order_create_line(product, item) ret = super(ReSaleOrder, self).sale_order_create_line(product, item)
# vals = { vals = {
# 'sale_order_id': ret.order_id.id, 'sale_order_id': ret.order_id.id,
# 'sale_order_line_id': ret.id, 'sale_order_line_id': ret.id,
# } }
# demand_plan_info = self.env['sf.demand.main.plan'].sudo().create(vals) demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
# vals.update({'demand_plan_id': demand_plan_info.id, 'plan_uom_qty': ret.product_uom_qty})
# demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
# demand_plan_info.write({'line_ids': demand_plan.ids})
# 优化方案1使用事务确保数据一致性
with self.env.cr.savepoint():
# 1. 先创建主计划
demand_plan_info = self.env['sf.demand.main.plan'].sudo().create({
'sale_order_id': ret.order_id.id,
'sale_order_line_id': ret.id,
})
# 2. 创建明细计划时直接建立关联利用One2many的inverse特性
demand_plan = self.env['sf.production.demand.plan'].sudo().create({
'demand_plan_id': demand_plan_info.id,
'plan_uom_qty': ret.product_uom_qty,
'sale_order_id': ret.order_id.id,
'sale_order_line_id': ret.id,
})
# 3. 不需要手动更新line_idsOdoo的ORM会自动处理One2many关系
if demand_plan.product_id.machining_drawings_name: if demand_plan.product_id.machining_drawings_name:
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0] filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
wizard_vals = { wizard_vals = {
@@ -65,23 +26,4 @@ class ReSaleOrder(models.Model):
'type': '1', 'type': '1',
} }
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals) self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
ret.order_id.demand_plan_ids = [(4, demand_plan_info.id)]
return ret return ret
#从sf工厂调过来时调用了这里重写
def confirm_to_supply_method(self):
self.state = 'sale'
for line in self.order_line:
if line.product_id.auto_machining:
line.supply_method = 'automation'
#在销售订单打开需求计划列表
def action_view_demand_plan(self):
self.ensure_one()
demand_plan_ids = self.env['sf.production.demand.plan'].sudo().search([('sale_order_id', '=', self.id)]).ids
return {
'res_model': 'sf.production.demand.plan',
'type': 'ir.actions.act_window',
'name': _("需求计划"),
'domain': [('id', 'in', demand_plan_ids)],
'view_mode': 'tree',
}

View File

@@ -1,206 +0,0 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.tools import float_compare
from odoo.exceptions import ValidationError
class SfDemandMainPlan(models.Model):
_name = 'sf.demand.main.plan'
_description = 'sf_demand_main_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
state = fields.Selection([
('10', '需求确认'),
('20', '待工艺设计'),
('30', '部分下达'),
('40', '已下达'),
('50', '取消'),
], string='状态', default='10', compute='_compute_state', store=True)
line_ids = fields.One2many(comodel_name='sf.production.demand.plan',
inverse_name='demand_plan_id', string="需求计划", copy=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)
product_id = fields.Many2one(
comodel_name='product.product',
related='sale_order_line_id.product_id',
string='产品', store=True, index=True)
part_name = fields.Char('零件名称', related='product_id.part_name')
part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类',
related='product_id.blank_type')
embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_long', store=True)
is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True)
pending_qty = fields.Float(
string="待计划",
compute='_compute_pending_qty', store=True)
planned_qty = fields.Float(
string="已计划",
compute='_compute_planned_qty', store=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
customer_name = fields.Char('客户', related='sale_order_id.customer_name')
product_uom_qty = fields.Float(
string="需求数量",
related='sale_order_line_id.product_uom_qty', store=True)
deadline_of_delivery = fields.Date('客户交期', related='sale_order_line_id.delivery_end_date', store=True)
contract_date = fields.Date('合同日期', related='sale_order_id.contract_date')
contract_code = fields.Char('合同号', related='sale_order_id.contract_code', store=True)
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
'demand_plan_process_parameter_rel',
string='表面工艺',
compute='_compute_model_process_parameters_ids'
, store=True
)
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度',
related='product_id.model_machining_precision')
inventory_quantity_auto_apply = fields.Float(
string="成品库存",
compute='_compute_inventory_quantity_auto_apply', store=True
)
priority = fields.Selection([
('1', '紧急'),
('2', ''),
('3', ''),
('4', ''),
], string='优先级', default='3')
hide_button_release_plan = fields.Boolean(
string='显示下达计划按钮',
compute='_compute_hide_button_release_plan',
default=False
)
@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.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_long', 'product_id.model_width', 'product_id.model_height')
def _compute_embryo_long(self):
for line in self:
if line.product_id:
line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}"
else:
line.embryo_long = 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('product_uom_qty', 'line_ids.plan_uom_qty')
def _compute_pending_qty(self):
for line in self:
sum_plan_uom_qty = sum(line.line_ids.mapped('plan_uom_qty'))
pending_qty = line.product_uom_qty - sum_plan_uom_qty
rounding = line.product_id.uom_id.rounding or 0.01#设置默认精度
# 如果精度小于等于0则设置为0.01
if rounding<=0:
rounding = 0.01
# 如果计划量小于等于0则设置为0
if float_compare(pending_qty, 0, precision_rounding=rounding) == -1:
line.pending_qty = 0
else:
line.pending_qty = pending_qty
@api.depends('line_ids.plan_uom_qty')
def _compute_planned_qty(self):
for line in self:
line.planned_qty = sum(line.line_ids.mapped('plan_uom_qty'))
@api.depends('line_ids.status')
def _compute_hide_button_release_plan(self):
for line in self:
line.hide_button_release_plan = bool(line.line_ids.filtered(lambda p: p.status != '60'))
def button_release_plan(self):
pass
@api.depends('line_ids.status', 'sale_order_id.state')
def _compute_state(self):
for line in self:
#已下达的订单
status_line = line.line_ids.filtered(lambda p: p.status == '60')
if line.sale_order_id.state == 'cancel':
line.state = '50'
line.line_ids.status = '100'
elif len(line.line_ids) == len(status_line):
line.state = '40'
#排除上面全部下达的情形后,如果有已下达订单,则状态为部分
elif bool(status_line):
line.state = '30'
else:
line.state = '10'
def write(self, vals):
res = super(SfDemandMainPlan, self).write(vals)
line_ids = self.line_ids.filtered(lambda p: p.plan_uom_qty == 0 or p.plan_uom_qty < 0)
if line_ids:
raise ValidationError(f"计划量不能小于等于0")
if 'line_ids' in vals:
for line in self.line_ids:
if not line.sale_order_id:
line.sale_order_id = self.sale_order_id
if not line.sale_order_line_id:
line.sale_order_line_id = self.sale_order_line_id
return res
def name_get(self):
result = []
for plan in self:
result.append((plan.id, plan.sale_order_id.name))
return result

File diff suppressed because it is too large Load Diff

View File

@@ -1,53 +0,0 @@
from odoo import models, fields, api, _
class SfStockRoute(models.Model):
_inherit = 'stock.route'
demand_plan_selectable = fields.Boolean("需求计划行")
stock_route_group_ids = fields.Many2many('stock.route.group', 'route_to_group', string='路线组')
demand_plan_ids = fields.Many2many('sf.production.demand.plan', 'stock_route_demand_plan', 'route_id',
'demand_plan_id', '需求计划', copy=False, compute='_compute_demand_plan_ids',
store=True)
@api.depends('demand_plan_selectable', 'stock_route_group_ids')
def _compute_demand_plan_ids(self):
for sr in self:
if sr.demand_plan_selectable:
stock_route_group = [srg.code for srg in sr.stock_route_group_ids]
demand_plan_ids = self.env['sf.production.demand.plan'].sudo().search(
[('supply_method', 'in', stock_route_group)])
if demand_plan_ids:
sr.demand_plan_ids = demand_plan_ids.ids
<<<<<<< HEAD
continue
=======
break
>>>>>>> develop
sr.demand_plan_ids = None
# def name_get(self):
# res = super().name_get()
# if self.env.context.get('demand_plan_search_stock_route_id'):
# demand_plan_id = self.env['sf.production.demand.plan'].sudo().browse(
# int(self.env.context.get('demand_plan_search_stock_route_id')))
# if demand_plan_id and demand_plan_id.supply_method:
# supply_method = self._set_supply_method(demand_plan_id.supply_method)
# res = [(item[0], f'{item[1]}-{supply_method}') for item in res if len(item) == 2]
# return res
#
# def _set_supply_method(self, supply_method):
# return {
# 'automation': "自动化产线加工",
# 'manual': "人工线下加工",
# 'purchase': "外购",
# 'outsourcing': "委外加工"
# }.get(supply_method)
class SfStockRouteGroup(models.Model):
_name = 'stock.route.group'
_description = '路线组'
name = fields.Char('名称')
code = fields.Char('编码')

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class StockRule(models.Model):
_inherit = 'stock.rule'
def _prepare_mo_vals(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values,
bom):
res = super()._prepare_mo_vals(product_id, product_qty, product_uom, location_id, name, origin, company_id,
values, bom)
if self.env.context.get('demand_plan_line_id'):
res['demand_plan_line_id'] = self.env.context.get('demand_plan_line_id')
return res
@api.model
def _prepare_purchase_request_line(self, request_id, procurement):
res = super()._prepare_purchase_request_line(request_id, procurement)
if self.env.context.get('demand_plan_line_id'):
res['demand_plan_line_id'] = self.env.context.get('demand_plan_line_id')
return res

View File

@@ -1,20 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 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,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,1,1 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,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 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
<<<<<<< HEAD
access_sf_demand_main_plan,sf.demand.main.plan,model_sf_demand_main_plan,base.group_user,1,0,0,0
access_sf_demand_main_plan_for_dispatch,sf.demand.main.plan for dispatch,model_sf_demand_main_plan,sf_base.group_plan_dispatch,1,1,0,0
access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0
access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0
access_sf_release_plan_wizard,sf.release.plan.wizard,model_sf_release_plan_wizard,base.group_user,1,0,0,0
access_sf_release_plan_wizard_for_dispatch,sf.release.plan.wizard for dispatch,model_sf_release_plan_wizard,sf_base.group_plan_dispatch,1,1,1,1
=======
access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0
access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0
>>>>>>> develop
1 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 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 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,1,1 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 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 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
6 <<<<<<< HEAD
access_sf_demand_main_plan,sf.demand.main.plan,model_sf_demand_main_plan,base.group_user,1,0,0,0
access_sf_demand_main_plan_for_dispatch,sf.demand.main.plan for dispatch,model_sf_demand_main_plan,sf_base.group_plan_dispatch,1,1,0,0
access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0
access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0
access_sf_release_plan_wizard,sf.release.plan.wizard,model_sf_release_plan_wizard,base.group_user,1,0,0,0
access_sf_release_plan_wizard_for_dispatch,sf.release.plan.wizard for dispatch,model_sf_release_plan_wizard,sf_base.group_plan_dispatch,1,1,1,1
=======
access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0
access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0
>>>>>>> develop

View File

@@ -82,8 +82,3 @@
cursor: pointer; cursor: pointer;
} }
} }
th[data-name=processing_time] + th::before{
content: '待执行单据';
line-height: 38px;
}

View File

@@ -1,70 +0,0 @@
<odoo>
<record id="view_sf_demand_main_plan_form" model="ir.ui.view">
<field name="name">sf.demand.main.plan.form</field>
<field name="model">sf.demand.main.plan</field>
<field name="arch" type="xml">
<form>
<header>
<field name="hide_button_release_plan" invisible="1"/>
<!-- <button string="下达计划" name="button_release_plan" type="object"-->
<!-- class="btn-primary" attrs="{'invisible': [('hide_button_release_plan', '=', False)]}"/>-->
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group>
<field name="product_id"/>
<field name="part_name"/>
<field name="part_number"/>
<field name="materials_id"/>
<field name="blank_type"/>
<field name="embryo_long"/>
<field name="is_incoming_material"/>
<field name="pending_qty"/>
<field name="planned_qty"/>
<field name="model_id"/>
</group>
<group>
<field name="customer_name"/>
<field name="product_uom_qty"/>
<field name="deadline_of_delivery"/>
<field name="contract_date"/>
<field name="contract_code"/>
<field name="model_process_parameters_ids" widget="many2many_tags"/>
<field name="model_machining_precision"/>
<field name="inventory_quantity_auto_apply"/>
<field name="priority" attrs="{'readonly': [('state', 'in', ('40','50'))]}"/>
</group>
</group>
<notebook>
<page string="计划">
<field name="line_ids" attrs="{'readonly': [('state', 'in', ('40','50'))]}">
<tree editable="bottom" delete="false">
<field name="status"/>
<field name="supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="route_ids" widget="many2many_tags" optional="hide"/>
<field name="location_id" optional="hide"/>
<field name="bom_id" optional="hide"/>
<field name="plan_uom_qty"/>
<field name="blank_arrival_date"/>
<field name="finished_product_arrival_date"/>
<field name="planned_start_date"/>
<field name="actual_start_date"/>
<field name="actual_end_date"/>
<field name="plan_remark"/>
<field name="procurement_reason"/>
<field name="write_date" string="更新时间"/>
<field name="hide_release_production_order" invisible="1"/>
<button string="下达计划" name="button_release_plan" type="object" class="btn-primary" attrs="{'invisible': [('status', 'in', ('50','60','100'))]}" />
<button name="button_release_production" type="object" string="下发生产" class="btn-primary" attrs="{'invisible': [('hide_release_production_order', '=', False)]}" />
<button name="button_delete" type="object" string="删除" class="btn-primary" attrs="{'invisible': [('status', 'not in', ('10','20','30'))]}" confirm='是否确认删除?'/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</odoo>

View File

@@ -4,13 +4,14 @@
<field name="model">sf.production.demand.plan</field> <field name="model">sf.production.demand.plan</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="需求计划" default_order="sequence desc,id desc" editable="bottom" <tree string="需求计划" default_order="sequence desc,id desc" editable="bottom"
class="demand_plan_tree freeze-columns-before-part_number" create="false" delete="false"> class="demand_plan_tree">
<header> <header>
<button string="打印" name="button_action_print" type="object" <button string="打印" name="button_action_print" type="object"
class="btn-primary"/> class="btn-primary"/>
</header> </header>
<field name="sequence" widget="handle"/> <field name="sequence" widget="handle"/>
<field name="id" optional="hide"/> <field name="id" optional="hide"/>
<field name="priority"/>
<field name="status"/> <field name="status"/>
<field name="customer_name"/> <field name="customer_name"/>
<field name="order_remark"/> <field name="order_remark"/>
@@ -20,58 +21,54 @@
<field name="part_name"/> <field name="part_name"/>
<field name="part_number"/> <field name="part_number"/>
<field name="is_incoming_material"/> <field name="is_incoming_material"/>
<field name="supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/> <field name="supply_method"/>
<field name="product_uom_qty"/> <field name="product_uom_qty"/>
<field name="deadline_of_delivery"/> <field name="deadline_of_delivery"/>
<field name="inventory_quantity_auto_apply"/> <field name="inventory_quantity_auto_apply"/>
<field name="qty_delivered"/> <field name="qty_delivered"/>
<field name="qty_to_deliver"/> <field name="qty_to_deliver"/>
<field name="model_long"/> <field name="model_long"/>
<field name="blank_type" optional="hide"/> <field name="blank_type"/>
<field name="blank_precision"/>
<field name="embryo_long"/> <field name="embryo_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="product_remark" optional="hide"/>
<field name="order_code" optional="hide"/> <field name="order_code" optional="hide"/>
<field name="sale_order_id" optional="hide"/> <field name="sale_order_id" optional="hide"/>
<field name="sale_order_line_number" optional="hide"/> <field name="sale_order_line_number" optional="hide"/>
<field name="order_state"/> <field name="order_state"/>
<field name="route_id" optional="hide"/>
<field name="contract_date"/> <field name="contract_date"/>
<field name="date_order"/> <field name="date_order"/>
<field name="contract_code"/> <field name="contract_code"/>
<field name="plan_remark" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/> <field name="plan_remark"/>
<field name="priority" decoration-danger="priority == '1'" <field name="processing_time"/>
decoration-warning="priority == '2'"
decoration-info="priority == '3'"
decoration-success="priority == '4'"/>
<field name="material_check" optional="hide"/> <field name="material_check" optional="hide"/>
<!-- <field name="hide_action_open_mrp_production" invisible="1"/>--> <field name="hide_action_open_mrp_production" invisible="1"/>
<!-- <field name="hide_action_purchase_orders" invisible="1"/>--> <field name="hide_action_purchase_orders" invisible="1"/>
<!-- <field name="hide_action_stock_picking" invisible="1"/>--> <field name="hide_action_stock_picking" invisible="1"/>
<!-- <field name="hide_action_view_programming" invisible="1"/>--> <field name="hide_action_view_programming" invisible="1"/>
<!-- <button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"--> <button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"
<!-- attrs="{'invisible': [('supply_method', '!=', False)]}"/>--> attrs="{'invisible': [('supply_method', '!=', False)]}"/>
<!-- <button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"--> <button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"
<!-- attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>--> attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>
<!-- <button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"--> <button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"
<!-- attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>--> attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>
<!-- <button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"--> <button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"
<!-- attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>--> attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>
<!-- <button name="action_view_programming" type="object" string="编程单" class="btn-secondary"--> <button name="action_view_programming" type="object" string="编程单" class="btn-secondary"
<!-- attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>--> attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>
<field name="planned_start_date" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/> <field name="planned_start_date"/>
<field name="actual_start_date"/> <field name="actual_start_date"/>
<field name="actual_end_date"/> <field name="actual_end_date"/>
<field name="processing_time"/>
<field name="create_date" optional="hide" string="创建时间"/> <field name="create_date" optional="hide" string="创建时间"/>
<field name="create_uid" optional="hide" string="创建人"/> <field name="create_uid" optional="hide" string="创建人"/>
<field name="write_date" string="更新时间"/> <field name="write_date" string="更新时间"/>
<field name="write_uid" optional="hide" string="更新人"/> <field name="write_uid" optional="hide" string="更新人"/>
<field name="print_count"/> <field name="print_count"/>
<field name="hide_release_production_order" invisible="1"/> <button name="release_production_order" type="object" string="下达生产" class="btn-primary"
<button name="button_release_production" type="object" string="下发生产" class="btn-primary" attrs="{'invisible': ['|',('status', '!=', '50'), ('supply_method', 'not in', ['automation', 'manual'])]}"/>
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
/>
<button name="action_edit_demand_plan_form" type="object" string="编辑" class="btn-primary"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -94,8 +91,7 @@
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/> <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_status" string="状态" domain="[]" context="{'group_by': 'status'}"/>
<filter name="group_by_customer_name" string="客户" domain="[]" <filter name="group_by_customer_name" string="客户" domain="[]" context="{'group_by': 'customer_name'}"/>
context="{'group_by': 'customer_name'}"/>
<filter name="group_by_is_incoming_material" string="客供料" domain="[]" <filter name="group_by_is_incoming_material" string="客供料" domain="[]"
context="{'group_by': 'is_incoming_material'}"/> context="{'group_by': 'is_incoming_material'}"/>
<filter name="group_by_supply_method" string="供货方式" domain="[]" <filter name="group_by_supply_method" string="供货方式" domain="[]"
@@ -110,6 +106,7 @@
</search> </search>
</field> </field>
</record> </record>
<record id="sf_production_demand_plan_action" model="ir.actions.act_window"> <record id="sf_production_demand_plan_action" model="ir.actions.act_window">
<field name="name">需求计划</field> <field name="name">需求计划</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
@@ -117,4 +114,12 @@
<field name="view_mode">tree</field> <field name="view_mode">tree</field>
</record> </record>
<menuitem
id="demand_plan_menu"
name="需求计划"
sequence="140"
action="sf_production_demand_plan_action"
parent="sf_plan.sf_production_plan_menu"
/>
</odoo> </odoo>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<menuitem
id="demand_plan_menu"
name="需求计划"
sequence="140"
action="sf_demand_plan.sf_production_demand_plan_action"
parent="sf_plan.sf_production_plan_menu"
/>
<!-- 调拨动作中屏蔽验证-->
<record id="stock.action_validate_picking" model="ir.actions.server">
<field name="binding_model_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_order_form_inherit_plan" model="ir.ui.view">
<field name="name">view.sale.order.form.inherit.plan</field>
<field name="inherit_id" ref="sf_manufacturing.view_order_form_inherit_supply_method"/>
<field name="model">sale.order</field>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='action_confirm'][last()]" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//page/field[@name='order_line']/tree/field[@name='supply_method']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button" name="action_view_demand_plan" type="object" icon="fa-pencil-square-o"
attrs="{'invisible': [('demand_plan_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="demand_plan_count"/>
</span>
<span class="o_stat_text">需求计划</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="sf_stock_location_route_form_view" model="ir.ui.view">
<field name="name">stock.route.form</field>
<field name="model">stock.route</field>
<field name="inherit_id" ref="stock.stock_location_route_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='packaging_selectable']" position="after">
<field name="demand_plan_selectable"/>
</xpath>
<xpath expr="//group[@name='route_selector']" position="after">
<group name="group_category" string="组类">
<group>
<field name="stock_route_group_ids" options="{'no_create': True}" widget="many2many_tags"/>
<field name="demand_plan_ids" invisible="1" options="{'no_create': True}" widget="many2many_tags"/>
</group>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,2 +1 @@
from . import sf_demand_plan_print_wizard from . import sf_demand_plan_print_wizard
from . import sf_release_plan_wizard

View File

@@ -4,12 +4,12 @@
<field name="name">sf.demand.plan.print.wizard.tree</field> <field name="name">sf.demand.plan.print.wizard.tree</field>
<field name="model">sf.demand.plan.print.wizard</field> <field name="model">sf.demand.plan.print.wizard</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="打印" class="print_demand" js_class="print_demand"> <tree string="打印" class="print_demand" js_class="print_demand" >
<field name="model_id"/> <field name="model_id"/>
<field name="filename_url"/> <field name="filename_url"/>
<field name="type"/> <field name="type"/>
<field name="machining_drawings" attrs="{'column_invisible': True }"/> <field name="machining_drawings" attrs="{'column_invisible': True }"/>
<field name="cnc_worksheet" attrs="{'column_invisible': True }"/> <field name="cnc_worksheet" attrs="{'column_invisible': True }" />
<field name="status"/> <field name="status"/>
</tree> </tree>
</field> </field>

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
import logging
from odoo import models, fields, api, _
_logger = logging.getLogger(__name__)
class SfReleasePlanWizard(models.TransientModel):
_name = 'sf.release.plan.wizard'
_description = u'下达计划向导'
demand_plan_id = fields.Many2one(comodel_name="sf.demand.main.plan",
string="需求计划", readonly=True)
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
release_message = fields.Char(string='提示', readonly=True)
def confirm(self):
if self.demand_plan_line_id:
self.demand_plan_line_id.action_confirm()

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_release_plan_wizard_form">
<field name="name">sf.release.plan.wizard.form</field>
<field name="model">sf.release.plan.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<div>
<div style="white-space: pre-wrap;">
<field name="release_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

@@ -3,7 +3,6 @@ import logging
import re import re
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import ValidationError
class ResProductCategory(models.Model): class ResProductCategory(models.Model):
@@ -48,14 +47,11 @@ class ResMrpBomMo(models.Model):
item.subcontractor_name = '' item.subcontractor_name = ''
def bom_create_line_has(self, embryo): def bom_create_line_has(self, embryo):
product = self.product_tmpl_id
if product.unit_number in (0, None, False):
raise ValidationError(f'产品{product.name}单件用量的值不能为{product.unit_number}')
vals = { vals = {
'bom_id': self.id, 'bom_id': self.id,
'product_id': embryo.id, 'product_id': embryo.id,
'product_tmpl_id': embryo.product_tmpl_id.id, 'product_tmpl_id': embryo.product_tmpl_id.id,
'product_qty': product.unit_number, 'product_qty': 1,
'product_uom_id': 1 'product_uom_id': 1
} }
return self.env['mrp.bom.line'].sudo().create(vals) return self.env['mrp.bom.line'].sudo().create(vals)
@@ -126,7 +122,7 @@ class ResMrpBomMo(models.Model):
# 查bom的原材料 # 查bom的原材料
def get_raw_bom(self, product): def get_raw_bom(self, product):
raw_bom = self.env['product.product'].search( raw_bom = self.env['product.product'].search(
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)], limit=1) [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)],limit=1)
return raw_bom return raw_bom

View File

@@ -95,8 +95,6 @@
<page string="加工参数"> <page string="加工参数">
<group> <group>
<group string="模型"> <group string="模型">
<field name="blank_type" readonly="1"/>
<field name="blank_precision" readonly="1"/>
<label for="model_long" string="坯料尺寸[mm]"/> <label for="model_long" string="坯料尺寸[mm]"/>
<div class="o_address_format"> <div class="o_address_format">
<label for="model_long" string="长"/> <label for="model_long" string="长"/>
@@ -106,7 +104,7 @@
<label for="model_height" string="高"/> <label for="model_height" string="高"/>
<field name="model_height" class="o_address_zip"/> <field name="model_height" class="o_address_zip"/>
</div> </div>
<field name="unit_number" readonly="1"/> <field name="blank_type" readonly="1"/>
<field name="model_volume" string="体积[mm³]"/> <field name="model_volume" string="体积[mm³]"/>
<field name="product_model_type_id" string="模型类型"/> <field name="product_model_type_id" string="模型类型"/>
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板" <field name="model_processing_panel" placeholder="例如R,U" string="加工面板"

View File

@@ -437,7 +437,7 @@ class Sf_Dashboard_Connect(http.Controller):
('state', 'in', ['ready', 'progress', 'done']) ('state', 'in', ['ready', 'progress', 'done'])
]) ])
plan_data_total_counts = sum(plan_data_total.mapped('qty_production')) plan_data_total_counts = sum(plan_data_total.mapped('qty_produced'))
# # 工单完成量 # # 工单完成量
# plan_data_finish_counts = plan_obj.search_count( # plan_data_finish_counts = plan_obj.search_count(
@@ -489,7 +489,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单返工数量 # 工单返工数量
plan_data_rework = work_order_obj.search(work_order_domain + [ plan_data_rework = work_order_obj.search(plan_domain + [
('state', 'in', ['rework']) ('state', 'in', ['rework'])
]) ])
@@ -601,11 +601,8 @@ class Sf_Dashboard_Connect(http.Controller):
line_list = ast.literal_eval(kw['line_list']) line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"') begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"') end_time_str = kw['end_time'].strip('"')
# 将时间减去8小时UTC+8转UTC begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
begin_time = (datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8)) end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
end_time = (datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8))
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# print('line_list: %s' % line_list) # print('line_list: %s' % line_list)
print('kw', kw) print('kw', kw)
time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天 time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
@@ -640,15 +637,6 @@ class Sf_Dashboard_Connect(http.Controller):
if time_unit == 'hour': if time_unit == 'hour':
# 计划量目前只能从mail.message中筛选出
plan_order_messages = request.env['mail.message'].sudo().search([
('model', '=', 'mrp.workorder'),
('create_date', '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')),
('create_date', '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')),
('tracking_value_ids.field_desc', '=', '状态'),
('tracking_value_ids.new_value_char', '=', '就绪')
])
for line in line_list: for line in line_list:
date_field_name = 'date_finished' # 替换为你模型中的实际字段名 date_field_name = 'date_finished' # 替换为你模型中的实际字段名
order_counts = [] order_counts = []
@@ -690,10 +678,19 @@ class Sf_Dashboard_Connect(http.Controller):
) )
# 使用小时和分钟作为键,确保每个小时的数据有独立的键 # 使用小时和分钟作为键,确保每个小时的数据有独立的键
key = (start_time + timedelta(hours=8)).strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
# time_count_dict[key] = len(orders) # time_count_dict[key] = len(orders)
time_count_dict[key] = sum(interval_orders.mapped('qty_produced')) time_count_dict[key] = sum(interval_orders.mapped('qty_produced'))
# 计划量目前只能从mail.message中筛选出
plan_order_messages = request.env['mail.message'].sudo().search([
('model', '=', 'mrp.workorder'),
('create_date', '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')),
('create_date', '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')),
('tracking_value_ids.field_desc', '=', '状态'),
('tracking_value_ids.new_value_char', '=', '就绪')
])
for time_interval in time_intervals: for time_interval in time_intervals:
start_time, end_time = time_interval start_time, end_time = time_interval
@@ -709,9 +706,7 @@ class Sf_Dashboard_Connect(http.Controller):
and o.create_date <= end_time and o.create_date <= end_time
) )
interval_order_ids = set(interval_plan_orders.mapped('res_id')) interval_orders = request.env['mrp.workorder'].sudo().browse(interval_plan_orders.mapped('res_id'))
interval_orders = request.env['mrp.workorder'].sudo().browse(interval_order_ids)
if line == '业绩总览': if line == '业绩总览':
interval_orders = interval_orders.filtered(lambda o: o.routing_type in ['人工线下加工', 'CNC加工']) interval_orders = interval_orders.filtered(lambda o: o.routing_type in ['人工线下加工', 'CNC加工'])
elif line == '人工线下加工中心': elif line == '人工线下加工中心':
@@ -720,9 +715,9 @@ class Sf_Dashboard_Connect(http.Controller):
interval_orders = interval_orders.filtered(lambda o: o.routing_type == 'CNC加工' and o.production_line_id.name == line) interval_orders = interval_orders.filtered(lambda o: o.routing_type == 'CNC加工' and o.production_line_id.name == line)
# 使用小时和分钟作为键,确保每个小时的数据有独立的键 # 使用小时和分钟作为键,确保每个小时的数据有独立的键
key = (start_time + timedelta(hours=8)).strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
# time_count_dict[key] = len(orders) # time_count_dict[key] = len(orders)
plan_count_dict[key] = sum(interval_orders.mapped('qty_production')) plan_count_dict[key] = sum(interval_orders.mapped('qty_produced'))
# order_counts.append() # order_counts.append()
res['data'][line] = { res['data'][line] = {
@@ -862,39 +857,24 @@ class Sf_Dashboard_Connect(http.Controller):
:param kw: :param kw:
:return: :return:
""" """
request.env['stock.warehouse'].browse(request.env.company.id).pbm_loc_id
# res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []} # res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []}
res = {'status': 1, 'message': '成功', 'data': {}} res = {'status': 1, 'message': '成功', 'data': {}}
# 解决产品名称取到英文的问题
request.update_context(lang='zh_CN')
plan_obj = request.env['sf.production.plan'].sudo() plan_obj = request.env['sf.production.plan'].sudo()
work_order_obj = request.env['mrp.workorder'].sudo() work_order_obj = request.env['mrp.workorder'].sudo()
# 获取mrp.workorder的state字段的selection内容
state_dict = dict(request.env['mrp.workorder'].sudo()._fields['state'].selection)
line_list = ast.literal_eval(kw['line_list']) line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"') begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"') end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# print('line_list: %s' % line_list) # print('line_list: %s' % line_list)
not_done_data = []
done_data = []
final_data = {} final_data = {}
not_done_index = 1
# 获取当前时间并计算24小时前的时间 done_index = 1
current_time = datetime.now()
time_48_hours_ago = current_time - timedelta(hours=48)
# # 计划量目前只能从mail.message中筛选出
# plan_order_messages = request.env['mail.message'].sudo().search([
# ('model', '=', 'mrp.workorder'),
# ('create_date', '>=', time_48_hours_ago.strftime('%Y-%m-%d %H:%M:%S')),
# ('tracking_value_ids.field_desc', '=', '状态'),
# ('tracking_value_ids.new_value_char', 'in', ['就绪', '生产中'])
# ])
for line in line_list: for line in line_list:
not_done_data = []
done_data = []
not_done_index = 1
done_index = 1
if line == '业绩总览': if line == '业绩总览':
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
@@ -910,24 +890,21 @@ class Sf_Dashboard_Connect(http.Controller):
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished']), # [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
# ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True) # ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
# ]) # ])
not_done_orders = work_order_obj.search(work_order_domain + [ not_done_orders = work_order_obj.search(work_order_domain +
('state', 'in', ['ready', 'progress']), [('state', 'in', ['ready', 'progress'])], order='id asc'
('date_planned_start', '>=', time_48_hours_ago),
('date_planned_start', '<=', current_time)
], order='id asc'
) )
# 完成订单 # 完成订单
# 获取当前时间并计算24小时前的时间 # 获取当前时间并计算24小时前的时间
# current_time = datetime.now() current_time = datetime.now()
# time_24_hours_ago = current_time - timedelta(hours=24) time_24_hours_ago = current_time - timedelta(hours=24)
finish_orders = work_order_obj.search(work_order_domain + [ finish_orders = work_order_obj.search(work_order_domain + [
('state', 'in', ['done']), ('state', 'in', ['finished']),
('production_id.state', 'not in', ['cancel']), ('production_id.state', 'not in', ['cancel']),
('date_finished', '>=', time_48_hours_ago) ('date_finished', '>=', time_24_hours_ago)
], order='id asc') ], order='id asc')
# logging.info('完成订单: %s' % finish_orders) # print(finish_orders)
# 获取所有未完成订单的ID列表 # 获取所有未完成订单的ID列表
order_ids = [order.id for order in not_done_orders] order_ids = [order.id for order in not_done_orders]
@@ -963,6 +940,14 @@ class Sf_Dashboard_Connect(http.Controller):
material_match = re.search(material_pattern, blank_name) material_match = re.search(material_pattern, blank_name)
material = material_match.group(1) if material_match else 'No match found' material = material_match.group(1) if material_match else 'No match found'
state_dict = {
'draft': '待排程',
'done': '已排程',
'processing': '生产中',
'finished': '已完成',
'ready': '待加工',
'progress': '生产中',
}
line_dict = { line_dict = {
'sequence': not_done_index, 'sequence': not_done_index,
@@ -978,6 +963,8 @@ class Sf_Dashboard_Connect(http.Controller):
not_done_index += 1 not_done_index += 1
for finish_order in finish_orders: for finish_order in finish_orders:
if not finish_order.actual_end_time:
continue
blank_name = '' blank_name = ''
try: try:
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
@@ -993,13 +980,13 @@ class Sf_Dashboard_Connect(http.Controller):
line_dict = { line_dict = {
'sequence': done_index, 'sequence': done_index,
'workorder_name': finish_order.production_id.name, 'workorder_name': finish_order.name,
'blank_name': blank_name, 'blank_name': blank_name,
'material': material, 'material': material,
'dimensions': dimensions, 'dimensions': dimensions,
'order_qty': finish_order.qty_produced, 'order_qty': order.qty_produced,
'finish_time': finish_order.date_finished.strftime( 'finish_time': finish_order.actual_end_time.strftime(
'%Y-%m-%d %H:%M:%S') if finish_order.date_finished else ' ' '%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
} }
done_data.append(line_dict) done_data.append(line_dict)

View File

@@ -18,5 +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 bom
# from . import stock_warehouse_orderpoint # from . import stock_warehouse_orderpoint

View File

@@ -1,16 +0,0 @@
from odoo import models
from odoo.osv.expression import AND
class MrpBom(models.Model):
_inherit = 'mrp.bom'
def _bom_subcontract_find(self, product, picking_type=None, company_id=False, bom_type='subcontract', subcontractor=False):
domain = self._bom_find_domain(product, picking_type=picking_type, company_id=company_id, bom_type=bom_type)
if self.env.context.get('stock_picking') == 'outsourcing':
return self.search(domain, order='sequence, product_id, id', limit=1)
if subcontractor:
domain = AND([domain, [('subcontractor_ids', 'parent_of', subcontractor.ids)]])
return self.search(domain, order='sequence, product_id, id', limit=1)
else:
return self.env['mrp.bom']

View File

@@ -1709,7 +1709,6 @@ class MrpProduction(models.Model):
vals['procurement_group_id'] = product_group_id[product_id.id] vals['procurement_group_id'] = product_group_id[product_id.id]
else: else:
vals['procurement_group_id'] = is_custemer_group_id[key] vals['procurement_group_id'] = is_custemer_group_id[key]
return super(MrpProduction, self).create(vals_list) return super(MrpProduction, self).create(vals_list)
@api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id',

View File

@@ -27,15 +27,13 @@ class ResProductMo(models.Model):
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True) categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
model_name = fields.Char('模型名称') model_name = fields.Char('模型名称')
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类') blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类')
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型')
model_long = fields.Float('模型长(mm)', digits=(16, 3)) model_long = fields.Float('模型长(mm)', digits=(16, 3))
model_width = fields.Float('模型宽(mm)', digits=(16, 3)) model_width = fields.Float('模型宽(mm)', digits=(16, 3))
model_height = fields.Float('模型高(mm)', digits=(16, 3)) model_height = fields.Float('模型高(mm)', digits=(16, 3))
unit_number = fields.Float('单件用量', digits=(16, 3), default=1)
model_volume = fields.Float('模型体积(m³)') model_volume = fields.Float('模型体积(m³)')
model_area = fields.Float('模型表面积(m²)') model_area = fields.Float('模型表面积(m²)')
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度') model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
model_processing_panel = fields.Char('模型加工面板', default='') model_processing_panel = fields.Char('模型加工面板')
model_remark = fields.Char('模型备注说明') model_remark = fields.Char('模型备注说明')
length = fields.Float('长(mm)', digits=(16, 3)) length = fields.Float('长(mm)', digits=(16, 3))
width = fields.Float('宽(mm)', digits=(16, 3)) width = fields.Float('宽(mm)', digits=(16, 3))
@@ -903,17 +901,15 @@ class ResProductMo(models.Model):
vals = { vals = {
'name': product_name, 'name': product_name,
'blank_type': item.get('blank_type'), 'blank_type': item.get('blank_type'),
'blank_precision': item.get('blank_precision'),
'model_long': item.get('blank_length') if blank_bool else self.format_float(item['model_long'] + embryo_redundancy_id.long), 'model_long': item.get('blank_length') if blank_bool else self.format_float(item['model_long'] + embryo_redundancy_id.long),
'model_width': item.get('blank_width') if blank_bool else self.format_float(item['model_width'] + embryo_redundancy_id.width), 'model_width': item.get('blank_width') if blank_bool else self.format_float(item['model_width'] + embryo_redundancy_id.width),
'model_height': item.get('blank_height') if blank_bool else self.format_float(item['model_height'] + embryo_redundancy_id.height), 'model_height': item.get('blank_height') if blank_bool else self.format_float(item['model_height'] + embryo_redundancy_id.height),
'unit_number': item.get('unit_number'),
'model_volume': self.format_float(((item['model_long'] + embryo_redundancy_id.long) * 'model_volume': self.format_float(((item['model_long'] + embryo_redundancy_id.long) *
(item['model_width'] + embryo_redundancy_id.width) * (item['model_width'] + embryo_redundancy_id.width) *
(item['model_height'] + embryo_redundancy_id.height))) if not blank_bool else ( (item['model_height'] + embryo_redundancy_id.height))) if not blank_bool else (
item.get('blank_length') * item.get('blank_width') * item.get('blank_height')), item.get('blank_length') * item.get('blank_width') * item.get('blank_height')),
'product_model_type_id': model_type.id, 'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'] if item['processing_panel_detail'] else '', 'model_processing_panel': item['processing_panel_detail'],
'model_machining_precision': item['model_machining_precision'], 'model_machining_precision': item['model_machining_precision'],
'model_code': item['barcode'], 'model_code': item['barcode'],
'length': item['model_long'], 'length': item['model_long'],

View File

@@ -21,7 +21,6 @@ from odoo.addons.sf_base.commons.common import Common
from odoo.exceptions import UserError from odoo.exceptions import UserError
from io import BytesIO from io import BytesIO
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta
class stockWarehouse(models.Model): class stockWarehouse(models.Model):
@@ -96,36 +95,35 @@ class StockRule(models.Model):
precision_rounding=proc[ precision_rounding=proc[
0].product_uom.rounding) > 0) 0].product_uom.rounding) > 0)
list2 = [] list2 = []
for procurement, rule in procurements: for item in procurements:
num = int(procurement.product_qty) num = int(item[0].product_qty)
warehouse_id = rule.warehouse_id product = self.env['product.product'].search(
if not warehouse_id: [("id", '=', item[0].product_id.id)])
warehouse_id = rule.location_dest_id.warehouse_id product_tmpl = self.env['product.template'].search(
manu_rule = rule.route_id.rule_ids.filtered(lambda r: r.action == 'manufacture' and r.warehouse_id == warehouse_id) ["&", ("id", '=', product.product_tmpl_id.id), ('single_manufacturing', "!=", False)])
if product_tmpl:
if procurement.product_id.product_tmpl_id.single_manufacturing and manu_rule:
if num > 1: if num > 1:
for no in range(1, num + 1): for no in range(1, num + 1):
Procurement = namedtuple('Procurement', ['product_id', 'product_qty', Procurement = namedtuple('Procurement', ['product_id', 'product_qty',
'product_uom', 'location_id', 'name', 'origin', 'product_uom', 'location_id', 'name', 'origin',
'company_id', 'company_id',
'values']) 'values'])
s = Procurement(product_id=procurement.product_id, product_qty=1.0, product_uom=procurement.product_uom, s = Procurement(product_id=item[0].product_id, product_qty=1.0, product_uom=item[0].product_uom,
location_id=procurement.location_id, location_id=item[0].location_id,
name=procurement.name, name=item[0].name,
origin=procurement.origin, origin=item[0].origin,
company_id=procurement.company_id, company_id=item[0].company_id,
values=procurement.values, values=item[0].values,
) )
# item1 = list(item) item1 = list(item)
# item1[0] = s item1[0] = s
list2.append((s, rule)) list2.append(tuple(item1))
else: else:
list2.append((procurement, rule)) list2.append(item)
else: else:
list2.append((procurement, rule)) list2.append(item)
for procurement, rule in list2: for procurement, rule in list2:
procure_method = rule.procure_method procure_method = rule.procure_method
@@ -185,6 +183,18 @@ class StockRule(models.Model):
'''创建制造订单''' '''创建制造订单'''
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values) productions_values)
# 将这一批制造订单的采购组根据成品设置为不同的采购组
# product_group_id = {}
# for index, production in enumerate(productions):
# if production.product_id.id not in product_group_id.keys():
# product_group_id[production.product_id.id] = production.procurement_group_id.id
# else:
# productions_values[index].update({'name': production.name})
# procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index])
# production.procurement_group_id = self.env["procurement.group"].create(procurement_group_vals).id
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
''' '''
创建工单 创建工单
@@ -728,33 +738,6 @@ class StockPicking(models.Model):
production.workorder_ids.write({'back_button_display': False}) production.workorder_ids.write({'back_button_display': False})
return res return res
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
subcontract_move.ensure_one()
group = self.env['procurement.group'].sudo().search([('name', '=', self.name)])
if not group:
group = self.env['procurement.group'].create({
'name': self.name,
'partner_id': self.partner_id.id,
})
product = subcontract_move.product_id
warehouse = self._get_warehouse(subcontract_move)
vals = {
'company_id': subcontract_move.company_id.id,
'procurement_group_id': group.id,
'subcontractor_id': subcontract_move.picking_id.partner_id.commercial_partner_id.id,
'picking_ids': [subcontract_move.picking_id.id],
'product_id': product.id,
'product_uom_id': subcontract_move.product_uom.id,
'bom_id': bom.id,
'location_src_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'product_qty': subcontract_move.product_uom_qty,
'picking_type_id': warehouse.subcontracting_type_id.id,
'date_planned_start': subcontract_move.date - relativedelta(days=product.produce_delay)
}
return vals
# 创建 外协出库入单 # 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders): def create_outcontract_picking(self, workorders, item, sorted_workorders):
production = workorders[0].production_id production = workorders[0].production_id
@@ -1233,20 +1216,6 @@ class ReStockMove(models.Model):
res['lot_id'] = self.subcontract_workorder_id.production_id.move_raw_ids.move_line_ids[0].lot_id.id res['lot_id'] = self.subcontract_workorder_id.production_id.move_raw_ids.move_line_ids[0].lot_id.id
return res return res
def _get_subcontract_bom(self):
self.ensure_one()
purchase_type = getattr(self.picking_id.purchase_id, 'purchase_type', False)
if purchase_type:
self = self.with_context(stock_picking=purchase_type)
bom = self.env['mrp.bom'].sudo()._bom_subcontract_find(
self.product_id,
picking_type=self.picking_type_id,
company_id=self.company_id.id,
bom_type='subcontract',
subcontractor=self.picking_id.partner_id
)
return bom
class ReStockQuant(models.Model): class ReStockQuant(models.Model):
_inherit = 'stock.quant' _inherit = 'stock.quant'

View File

@@ -200,11 +200,11 @@
<p></p> <p></p>
</div> </div>
</div> --> </div> -->
<!-- 页脚固定在底部 -->
<!-- <div style="position: absolute; bottom: 0; left: 0; right: 0;"> --> <!-- 页脚固定在底部 -->
<t t-call="sf_quality.report_quality_footer"/> <!-- <div style="position: absolute; bottom: 0; left: 0; right: 0;"> -->
<!-- </div> --> <t t-call="sf_quality.report_quality_footer"/>
<!-- </div> -->
</div> </div>
</t> </t>
</t> </t>
@@ -329,11 +329,9 @@
</div> --> </div> -->
<!-- 页脚固定在底部 --> <!-- 页脚固定在底部 -->
<!-- <t t-if="loop.index == len(docs) - 1">--> <!-- <div style="position: absolute; bottom: 0; left: 0; right: 0;"> -->
<!-- <div style="position: absolute; bottom: 0; left: 0; right: 0;"> --> <t t-call="sf_quality.html_report_quality_footer"/>
<t t-call="sf_quality.html_report_quality_footer"/> <!-- </div> -->
<!-- </div> -->
<!-- </t>-->
</div> </div>
</t> </t>
</t> </t>

View File

@@ -1,180 +1,59 @@
import logging
from odoo import api, models from odoo import api, models
from odoo.exceptions import ValidationError, UserError
class StockPicking(models.Model): class StockPicking(models.Model):
_inherit = 'stock.picking' _inherit = 'stock.picking'
def _compute_check(self):
super()._compute_check()
for picking in self:
picking_to_quality = picking.get_picking_to_quality()
if not picking_to_quality:
picking.quality_check_todo = False
break
else:
need_quality_line = picking.get_need_quality_line(picking_to_quality)
if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line):
picking.quality_check_todo = False
def check_quality(self):
self.ensure_one()
# checkable_products = self.mapped('move_line_ids').mapped('product_id')
# checks = self.check_ids.filtered(lambda check: check.quality_state == 'none' and (
# check.product_id in checkable_products or check.measure_on == 'operation'))
checks = self.env['quality.check']
picking_to_quality = self._get_picking_to_quality()
need_quality_line = self.get_need_quality_line(picking_to_quality)
if need_quality_line and any(line.get('need_done_check_ids') for line in need_quality_line):
for item in need_quality_line:
checks += item.get('need_done_check_ids')
if checks:
return checks.action_open_quality_check_wizard()
return False
def button_validate(self): def button_validate(self):
"""= """
出厂检验报告上传 出厂检验报告上传
""" """
out_quality_checks = self.env['quality.check'].search( out_quality_checks = self.env['quality.check'].search(
[('picking_id', '=', self.id), ('test_type_id.name', '=', '出厂检验报告'), [('picking_id', '=', self.id), ('test_type_id.name', '=', '出厂检验报告')])
('quality_state', '=', 'pass')])
# out_quality_checks 可能存在多个 # out_quality_checks 可能存在多个
if out_quality_checks: if out_quality_checks:
for out_quality_check in out_quality_checks: for out_quality_check in out_quality_checks:
if not out_quality_check.is_factory_report_uploaded: if not out_quality_check.is_factory_report_uploaded:
if out_quality_check and self.state == 'assigned': if out_quality_check and self.state == 'assigned':
out_quality_check.upload_factory_report() out_quality_check.upload_factory_report()
quality_action = self.pinking_checkout_quality()
if quality_action:
return quality_action
res = super(StockPicking, self).button_validate()
return res
def pinking_checkout_quality(self):
""" """
调拨单若关联了质量检查单,验证调拨单时,应校验是否有不合格品,若存在,应弹窗提示: 调拨单若关联了质量检查单,验证调拨单时,应校验是否有不合格品,若存在,应弹窗提示:
“警告存在不合格产品XXXX n 件、YYYYY m件继续调拨请点“确认”否则请取消 “警告存在不合格产品XXXX n 件、YYYYY m件继续调拨请点“确认”否则请取消
""" """
try: context = self.env.context
self.ensure_one() if not context.get('again_validate') and self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail'):
context = self.env.context # 回滚事务,为二次确认/取消做准备
if not context.get('pinking_checkout_quality'): self.env.cr.rollback()
picking_to_quality = self._get_picking_to_quality() quality_check_ids = self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail')
if not picking_to_quality: return False product_list = list(set([quality_check_id.product_id for quality_check_id in quality_check_ids]))
need_quality_val = self.get_need_quality_line(picking_to_quality) fail_check_text = ''
if any(line.get('fail_check_ids') for line in need_quality_val): for product_id in product_list:
# 回滚事务,为二次确认/取消做准备 check_ids = quality_check_ids.filtered(lambda qc: qc.product_id == product_id)
self.env.cr.rollback() if all(check_id.measure_on == 'move_line' for check_id in check_ids):
# 获取存在失败的 质检单 调拨单明细行 number = sum(check_ids.mapped('qty_line'))
check_list = [item for item in need_quality_val if item.get('fail_check_ids')] else:
fail_check_text = '' number = sum(self.move_ids_without_package.filtered(
for item in check_list: lambda ml: ml.product_id == product_id).mapped('quantity_done'))
move_id, pre_done_qty = item.get('move_id'), item.get('pre_done_qty') if number == 0:
fail_check_text = (f'{fail_check_text}{move_id.product_id.display_name} {pre_done_qty}' number = sum(self.move_ids_without_package.filtered(
if fail_check_text != '' else f'{move_id.product_id.display_name} {pre_done_qty}') lambda ml: ml.product_id == product_id).mapped('reserved_availability'))
return { if number == 0:
'type': 'ir.actions.act_window', number = sum(self.move_ids_without_package.filtered(
'res_model': 'picking.validate.check.wizard', lambda ml: ml.product_id == product_id).mapped('product_uom_qty'))
'name': '质检不合格提示', fail_check_text = (f'{fail_check_text}{product_id.display_name} {number}'
'view_mode': 'form', if fail_check_text != '' else f'{product_id.display_name} {number}')
'target': 'new', return {
'context': { 'type': 'ir.actions.act_window',
'default_picking_id': self.id, 'res_model': 'picking.validate.check.wizard',
'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', 'name': '质检不合格提示',
'pinking_checkout_quality': True} 'view_mode': 'form',
} 'target': 'new',
else: 'context': {
return False 'default_picking_id': self.id,
except Exception as e: 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?',
logging.info('pinking_checkout_quality()方法报错:%s' % e) 'again_validate': True}
raise ValidationError('调拨单验证质检单是否合格时报错,请联系管理员处理!!') }
res = super(StockPicking, self).button_validate()
def get_need_quality_line(self, picking_to_quality):
"""
# 对需要进行质检,还没有质检完成的明细行进行统计
# 1、当【质量标准_控制方式】=“产品、作业”,仅校验“预完成数量”>0的产品行对应的质检单必须处理。
2、当【质量标准_控制方式】=“数量”时,仅校验“预完成数量”>0的产品行对应的质检单必须处理
1每一类的“总单数”=【调拨单_需求】时则已处理的质检单“单数”≥“预完成数量”时可执行调拨单验证
2每一类的“总单数”<【调拨单_需求】时则已处理的质检单“单数”≥0时可执行调拨单验证
"""
res = []
for item in picking_to_quality:
need_done_check_ids = self.env['quality.check']
fail_check_ids = self.env['quality.check']
move_id, pre_done_qty, check_ids = item.values()
check_ids_1 = check_ids.filtered(lambda qc: qc.measure_on in ('operation', 'product'))
if check_ids_1:
check_ids_1_done = check_ids_1.filtered(lambda qc: qc.quality_state in ('pass', 'fail'))
check_ids_1_fail = check_ids_1.filtered(lambda qc: qc.quality_state == 'fail')
check_ids_1_none = check_ids_1.filtered(lambda qc: qc.quality_state == 'none')
if check_ids_1 and not check_ids_1_done:
need_done_check_ids += check_ids_1_none
if check_ids_1_fail:
fail_check_ids += check_ids_1_fail
check_ids_2 = check_ids.filtered(lambda qc: qc.measure_on == 'move_line')
if check_ids_2:
check_ids_2_done = check_ids_2.filtered(lambda qc: qc.quality_state in ('pass', 'fail'))
check_ids_2_fail = check_ids_2.filtered(lambda qc: qc.quality_state == 'fail')
check_ids_2_none = check_ids_2.filtered(lambda qc: qc.quality_state == 'none')
# 每一类的“总单数”=【调拨单_需求】时则已处理的质检单“单数”≥“预完成数量”时可执行调拨单验证
if len(check_ids_2) >= move_id.product_uom_qty and len(check_ids_2_done) < pre_done_qty:
need_done_check_ids += check_ids_2_none
# 每一类的“总单数”<【调拨单_需求】时则已处理的质检单“单数”≥0时可执行调拨单验证
elif len(check_ids_2) < move_id.product_uom_qty and len(check_ids_2_done) == 0:
need_done_check_ids += check_ids_2_none
if check_ids_2_fail:
fail_check_ids += check_ids_2_fail
if need_done_check_ids or fail_check_ids:
res.append({'move_id': move_id,
'pre_done_qty': pre_done_qty,
'check_ids': check_ids,
'fail_check_ids': fail_check_ids,
'need_done_check_ids': need_done_check_ids})
return res return res
def get_picking_to_quality(self):
self.ensure_one()
return self._get_picking_to_quality()
def _get_picking_to_quality(self):
"""
对需要质检的明细行进行统计(针对“预完成数量”>0的行)
"""
quality_piking_line_list = []
pre_done_qty_lines = self._get_pinking_pre_done_qty()
for line in pre_done_qty_lines:
move_id, pre_done_qty = line.values()
if pre_done_qty == 0:
continue
product_id = move_id.product_id
check_ids = self.check_ids.filtered(lambda c: c.product_id == product_id)
quality_piking_line_list.append({'move_id': move_id, 'pre_done_qty': pre_done_qty, 'check_ids': check_ids})
return quality_piking_line_list
def _get_pinking_pre_done_qty(self):
"""
return: 明细行 及 预完成数量
1、若调拨单所有明细行的【完成】=0且任意行的【预留】<【需求】,则验证时将会话是否需创建欠单。
---->此时“预完成数量”=【预留】
2、若调拨单任意行的0<【完成】<【需求】,且则验证时将会话是否需创建欠单
---->此时“预完成数量”=【完成】
"""
# if all(move_id.quantity_done == 0 for move_id in self.move_ids_without_package):
# pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.reserved_availability} for move_id in
# self.move_ids_without_package]
# else:
# pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.quantity_done} for move_id in
# self.move_ids_without_package]
pre_done_qty = []
for move_id in self.move_ids_without_package:
if move_id.quantity_done > 0:
pre_done_qty.append({'move_id': move_id, 'pre_done_qty': move_id.quantity_done})
else:
pre_done_qty.append({'move_id': move_id, 'pre_done_qty': move_id.reserved_availability})
return pre_done_qty