Compare commits
25 Commits
feature/根据
...
feature/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd6f35a16f | ||
|
|
86bacee926 | ||
|
|
e1644c3aa4 | ||
|
|
20df1c0365 | ||
|
|
20069d5336 | ||
|
|
fe8df494f9 | ||
|
|
0e7d6fac4e | ||
|
|
32cd68e15f | ||
|
|
70113cb3c5 | ||
|
|
f0e47371ed | ||
|
|
6dde814acc | ||
|
|
71ab241e94 | ||
|
|
272e424975 | ||
|
|
1fd9900e41 | ||
|
|
0a13acbb68 | ||
|
|
a68d6d4d06 | ||
|
|
c630074f4a | ||
|
|
e2ddac4fb0 | ||
|
|
e52ec3982f | ||
|
|
160bd117b7 | ||
|
|
4b026535f8 | ||
|
|
99ac89f995 | ||
|
|
35b1d648c3 | ||
|
|
fe3492ceb5 | ||
|
|
4274b9fe99 |
@@ -16,6 +16,7 @@ class PurchaseRequest(models.Model):
|
||||
)
|
||||
|
||||
rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True)
|
||||
rule_purchase_to_request = fields.Boolean('采购单根据规则创建坯料采购申请', default=False)
|
||||
|
||||
@api.depends('state')
|
||||
def _compute_state(self):
|
||||
|
||||
@@ -44,4 +44,15 @@ class StockPicking(models.Model):
|
||||
purchase_request_lines.move_dest_ids = [
|
||||
(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})
|
||||
|
||||
@@ -26,7 +26,7 @@ class StockRule(models.Model):
|
||||
request_data = rule._prepare_purchase_request(
|
||||
procurement.origin, procurement.values
|
||||
)
|
||||
request_data.update({'rule_new_add': True})
|
||||
request_data = self._update_request_data(procurement, request_data)
|
||||
pr = purchase_request_model.create(request_data)
|
||||
cache[domain] = pr
|
||||
elif (
|
||||
@@ -44,6 +44,18 @@ class StockRule(models.Model):
|
||||
request_line_data.update({'origin': procurement.origin})
|
||||
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):
|
||||
# 如果补货组相同,并且产品相同,则合并
|
||||
procurements_dict = defaultdict()
|
||||
|
||||
@@ -67,6 +67,16 @@
|
||||
<field name="part_number"/>
|
||||
<field name="part_name" invisible="1"/>
|
||||
</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>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -10,13 +10,21 @@
|
||||
""",
|
||||
'category': 'sf',
|
||||
'website': 'https://www.sf.jikimo.com',
|
||||
'depends': ['sf_plan','jikimo_printing'],
|
||||
'depends': ['sf_plan'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/stock_route_group.xml',
|
||||
<<<<<<< HEAD
|
||||
'views/sale_order_views.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_release_plan_wizard_views.xml',
|
||||
'views/menu_view.xml',
|
||||
],
|
||||
'demo': [
|
||||
],
|
||||
|
||||
25
sf_demand_plan/migrations/1.1/post-migrate.py
Normal file
25
sf_demand_plan/migrations/1.1/post-migrate.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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})
|
||||
@@ -1,5 +1,11 @@
|
||||
# -*- 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 sale_order
|
||||
from . import stock_route
|
||||
from . import stock_rule
|
||||
from . import purchase_request
|
||||
from . import purchase_order
|
||||
|
||||
|
||||
20
sf_demand_plan/models/mrp_bom.py
Normal file
20
sf_demand_plan/models/mrp_bom.py
Normal file
@@ -0,0 +1,20 @@
|
||||
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
|
||||
26
sf_demand_plan/models/mrp_production.py
Normal file
26
sf_demand_plan/models/mrp_production.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- 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
|
||||
22
sf_demand_plan/models/purchase_order.py
Normal file
22
sf_demand_plan/models/purchase_order.py
Normal file
@@ -0,0 +1,22 @@
|
||||
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'
|
||||
110
sf_demand_plan/models/purchase_request.py
Normal file
110
sf_demand_plan/models/purchase_request.py
Normal file
@@ -0,0 +1,110 @@
|
||||
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",
|
||||
}
|
||||
@@ -9,14 +9,53 @@ class ReSaleOrder(models.Model):
|
||||
compute='_compute_mrp_production_ids',
|
||||
string='与此销售订单相关联的制造订单',
|
||||
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):
|
||||
ret = super(ReSaleOrder, self).sale_order_create_line(product, item)
|
||||
vals = {
|
||||
'sale_order_id': ret.order_id.id,
|
||||
'sale_order_line_id': ret.id,
|
||||
}
|
||||
demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
|
||||
# vals = {
|
||||
# 'sale_order_id': ret.order_id.id,
|
||||
# 'sale_order_line_id': ret.id,
|
||||
# }
|
||||
# demand_plan_info = self.env['sf.demand.main.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_ids,Odoo的ORM会自动处理One2many关系
|
||||
if demand_plan.product_id.machining_drawings_name:
|
||||
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
|
||||
wizard_vals = {
|
||||
@@ -26,4 +65,23 @@ class ReSaleOrder(models.Model):
|
||||
'type': '1',
|
||||
}
|
||||
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
||||
ret.order_id.demand_plan_ids = [(4, demand_plan_info.id)]
|
||||
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',
|
||||
}
|
||||
|
||||
|
||||
206
sf_demand_plan/models/sf_demand_main_plan.py
Normal file
206
sf_demand_plan/models/sf_demand_main_plan.py
Normal file
@@ -0,0 +1,206 @@
|
||||
# -*- 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
|
||||
@@ -6,23 +6,29 @@ from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_compare
|
||||
from datetime import datetime, timedelta
|
||||
from odoo.exceptions import UserError
|
||||
import re
|
||||
|
||||
|
||||
class SfProductionDemandPlan(models.Model):
|
||||
_name = 'sf.production.demand.plan'
|
||||
_description = 'sf_production_demand_plan'
|
||||
def get_location_id(self):
|
||||
stock_location = self.env['stock.location'].sudo().search([('name', '=', '客户')], limit=1)
|
||||
return stock_location.id
|
||||
|
||||
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
|
||||
|
||||
demand_plan_id = fields.Many2one(comodel_name="sf.demand.main.plan",
|
||||
string="物料需求", readonly=True)
|
||||
priority = fields.Selection([
|
||||
('1', '紧急'),
|
||||
('2', '高'),
|
||||
('3', '中'),
|
||||
('4', '低'),
|
||||
], string='优先级', default='3')
|
||||
], string='优先级', related='demand_plan_id.priority')
|
||||
|
||||
status = fields.Selection([
|
||||
('10', '草稿'),
|
||||
('20', '待确认'),
|
||||
@@ -30,7 +36,21 @@ class SfProductionDemandPlan(models.Model):
|
||||
('50', '待下达生产'),
|
||||
('60', '已下达'),
|
||||
('100', '取消'),
|
||||
], string='状态', compute='_compute_status', store=True)
|
||||
], string='状态', default='30', readonly=True)
|
||||
|
||||
plan_uom_qty = fields.Float(string="计划量")
|
||||
procurement_reason = fields.Selection([
|
||||
('销售订单', "销售订单"),
|
||||
('需求预测', "需求预测"),
|
||||
('生产报废', "生产报废"),
|
||||
], string='补货原因', default='销售订单')
|
||||
|
||||
blank_arrival_date = fields.Date('采购计划到货(坯料)')
|
||||
finished_product_arrival_date = fields.Date('采购计划到货(成品)')
|
||||
bom_id = fields.Many2one('mrp.bom', string="BOM", readonly=True)
|
||||
location_id = fields.Many2one('stock.location', string='需求位置', default=get_location_id, readonly=True)
|
||||
|
||||
|
||||
sale_order_id = fields.Many2one(comodel_name="sale.order",
|
||||
string="销售订单", readonly=True)
|
||||
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line",
|
||||
@@ -49,21 +69,23 @@ class SfProductionDemandPlan(models.Model):
|
||||
string='产品', store=True, index=True)
|
||||
model_id = fields.Char('模型ID', related='product_id.model_id')
|
||||
part_name = fields.Char('零件名称', related='product_id.part_name')
|
||||
part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
|
||||
part_number = fields.Char('零件图号', related='demand_plan_id.part_number')
|
||||
#part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
|
||||
is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True)
|
||||
supply_method = fields.Selection([
|
||||
('automation', "自动化产线加工"),
|
||||
('manual', "人工线下加工"),
|
||||
('purchase', "外购"),
|
||||
('outsourcing', "委外加工"),
|
||||
], string='供货方式', related='sale_order_line_id.supply_method', store=True)
|
||||
], string='供货方式', default='manual')
|
||||
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)
|
||||
inventory_quantity_auto_apply = fields.Float(
|
||||
string="成品库存",
|
||||
compute='_compute_inventory_quantity_auto_apply'
|
||||
related='demand_plan_id.inventory_quantity_auto_apply'
|
||||
#compute='_compute_inventory_quantity_auto_apply'
|
||||
)
|
||||
qty_delivered = fields.Float(
|
||||
"交货数量", related='sale_order_line_id.qty_delivered')
|
||||
@@ -72,18 +94,39 @@ class SfProductionDemandPlan(models.Model):
|
||||
model_long = fields.Char('尺寸(mm)', compute='_compute_model_long')
|
||||
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类',
|
||||
related='product_id.blank_type')
|
||||
<<<<<<< HEAD
|
||||
#blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型', related='product_id.blank_precision')
|
||||
#materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
|
||||
embryo_long = fields.Char('坯料尺寸(mm)', related='demand_plan_id.embryo_long')
|
||||
materials_id = fields.Char('材料', related='demand_plan_id.materials_id')
|
||||
|
||||
=======
|
||||
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型', related='product_id.blank_precision')
|
||||
embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_long')
|
||||
materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
|
||||
>>>>>>> develop
|
||||
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度',
|
||||
related='product_id.model_machining_precision')
|
||||
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
|
||||
'plan_process_parameter_rel',
|
||||
string='表面工艺',
|
||||
compute='_compute_model_process_parameters_ids'
|
||||
, store=True
|
||||
)
|
||||
#model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
|
||||
# 'plan_process_parameter_rel',
|
||||
# string='表面工艺',
|
||||
# compute='_compute_model_process_parameters_ids'
|
||||
# , store=True
|
||||
# )
|
||||
model_process_parameters_ids = fields.Many2many(related='demand_plan_id.model_process_parameters_ids',
|
||||
string='表面工艺', )
|
||||
|
||||
product_remark = fields.Char("产品备注", related='product_id.model_remark')
|
||||
order_code = fields.Char('E-SHOP订单号', related='sale_order_id.order_code')
|
||||
<<<<<<< HEAD
|
||||
order_state = fields.Selection( string='订单状态',related='sale_order_line_id.state')
|
||||
|
||||
#route_id = fields.Many2one('stock.route', string='路线', related='sale_order_line_id.route_id', store=True)
|
||||
route_ids = fields.Many2many('stock.route', 'stock_route_demand_plan', 'demand_plan_id', 'route_id', '库存路线',
|
||||
domain=[('demand_plan_selectable', '=', True)], compute='_compute_route_ids',
|
||||
store=True)
|
||||
|
||||
=======
|
||||
order_state = fields.Selection(
|
||||
string='订单状态',
|
||||
related='sale_order_line_id.state')
|
||||
@@ -91,6 +134,7 @@ class SfProductionDemandPlan(models.Model):
|
||||
route_ids = fields.Many2many('stock.route', 'stock_route_demand_plan', 'demand_plan_id', 'route_id', '路线',
|
||||
domain=[('demand_plan_selectable', '=', True)], compute='_compute_route_ids',
|
||||
store=True)
|
||||
>>>>>>> develop
|
||||
contract_date = fields.Date('合同日期', related='sale_order_id.contract_date')
|
||||
date_order = fields.Datetime('下单日期', related='sale_order_id.date_order')
|
||||
contract_code = fields.Char('合同号', related='sale_order_id.contract_code', store=True)
|
||||
@@ -106,18 +150,62 @@ class SfProductionDemandPlan(models.Model):
|
||||
print_count = fields.Char('打印次数', default='T0C0', readonly=True)
|
||||
sequence = fields.Integer('序号')
|
||||
|
||||
hide_action_open_mrp_production = fields.Boolean(
|
||||
string='显示待工艺确认按钮',
|
||||
compute='_compute_hid_button',
|
||||
mrp_production_ids = fields.Many2many(
|
||||
'mrp.production',
|
||||
compute='_compute_mrp_production_ids',
|
||||
string='与此相需求计划关联的制造订单',
|
||||
store=True)
|
||||
hide_release_production_order = fields.Boolean(
|
||||
string='显示下发生产按钮',
|
||||
compute='_compute_hide_release_production_order',
|
||||
default=False
|
||||
)
|
||||
|
||||
hide_action_purchase_orders = fields.Boolean(
|
||||
string='显示采购按钮',
|
||||
compute='_compute_hide_action_purchase_orders',
|
||||
default=False
|
||||
)
|
||||
# hide_action_open_mrp_production = fields.Boolean(
|
||||
# string='显示待工艺确认按钮',
|
||||
# compute='_compute_hid_button',
|
||||
# default=False
|
||||
# )
|
||||
#
|
||||
# hide_action_purchase_orders = fields.Boolean(
|
||||
# string='显示采购按钮',
|
||||
# compute='_compute_hide_action_purchase_orders',
|
||||
# default=False
|
||||
# )
|
||||
#
|
||||
# hide_action_stock_picking = fields.Boolean(
|
||||
# string='显示调拨单按钮',
|
||||
# compute='_compute_hide_action_stock_picking',
|
||||
# default=False
|
||||
# )
|
||||
#
|
||||
# hide_action_outsourcing_stock_picking = fields.Boolean(
|
||||
# string='委外显示调拨单按钮',
|
||||
# compute='_compute_hide_action_stock_picking',
|
||||
# default=False
|
||||
# )
|
||||
#
|
||||
# hide_action_view_programming = fields.Boolean(
|
||||
# string='显示编程单按钮',
|
||||
# compute='_compute_hid_button',
|
||||
# default=False
|
||||
# )
|
||||
#
|
||||
# outsourcing_purchase_request = fields.Char('委外采购申请单')
|
||||
@api.depends('supply_method')
|
||||
def _compute_route_ids(self):
|
||||
for pdp in self:
|
||||
if pdp.supply_method:
|
||||
group_id = self.env['stock.route.group'].sudo().search([('code', '=', pdp.supply_method)])
|
||||
route_ids = self.env['stock.route'].sudo().search(
|
||||
[('demand_plan_selectable', '=', True), ('stock_route_group_ids', '=', group_id.id)])
|
||||
if route_ids:
|
||||
pdp.route_ids = route_ids.ids
|
||||
continue
|
||||
pdp.route_ids = None
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
hide_action_stock_picking = fields.Boolean(
|
||||
string='显示调拨单按钮',
|
||||
compute='_compute_hide_action_stock_picking',
|
||||
@@ -182,6 +270,7 @@ class SfProductionDemandPlan(models.Model):
|
||||
record.status = '60' # 已下达
|
||||
if sale_order_state == 'cancel' or not record.sale_order_line_id:
|
||||
record.status = '100' # 取消
|
||||
>>>>>>> develop
|
||||
|
||||
@api.depends('sale_order_line_id.product_id.name')
|
||||
def _compute_sale_order_line_number(self):
|
||||
@@ -191,18 +280,6 @@ class SfProductionDemandPlan(models.Model):
|
||||
else:
|
||||
line.sale_order_line_number = None
|
||||
|
||||
@api.depends('product_id.part_number', 'product_id.model_name')
|
||||
def _compute_part_number(self):
|
||||
for line in self:
|
||||
if line.product_id:
|
||||
if line.product_id.part_number:
|
||||
line.part_number = line.product_id.part_number
|
||||
else:
|
||||
if line.product_id.model_name:
|
||||
line.part_number = line.product_id.model_name.rsplit('.', 1)[0]
|
||||
else:
|
||||
line.part_number = None
|
||||
|
||||
@api.depends('product_id.length', 'product_id.width', 'product_id.height')
|
||||
def _compute_model_long(self):
|
||||
for line in self:
|
||||
@@ -211,6 +288,8 @@ class SfProductionDemandPlan(models.Model):
|
||||
else:
|
||||
line.model_long = None
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
@api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height', 'product_id.blank_type')
|
||||
def _compute_embryo_long(self):
|
||||
for line in self:
|
||||
@@ -259,6 +338,7 @@ class SfProductionDemandPlan(models.Model):
|
||||
else:
|
||||
line.inventory_quantity_auto_apply = 0.0
|
||||
|
||||
>>>>>>> develop
|
||||
@api.depends('sale_order_id.mrp_production_ids.workorder_ids.date_start')
|
||||
def _compute_actual_start_date(self):
|
||||
for record in self:
|
||||
@@ -321,17 +401,34 @@ class SfProductionDemandPlan(models.Model):
|
||||
else:
|
||||
record.material_check = None
|
||||
|
||||
@api.depends('status')
|
||||
def _compute_mrp_production_ids(self):
|
||||
for record in self:
|
||||
if record.status in ('50', '60'):
|
||||
record.mrp_production_ids = self.env['mrp.production'].sudo().search(
|
||||
[('demand_plan_line_id', '=', record.id)]).ids
|
||||
else:
|
||||
record.mrp_production_ids = None
|
||||
|
||||
@api.depends('mrp_production_ids.state')
|
||||
def _compute_hide_release_production_order(self):
|
||||
for record in self:
|
||||
# 检查这条需求计划所有制造订单的排程单状态,有一个为待排程状态,就显示该按钮
|
||||
record.hide_release_production_order = bool(record.mrp_production_ids.filtered(
|
||||
lambda p: p.state == 'confirmed'
|
||||
))
|
||||
@api.constrains('planned_start_date')
|
||||
def _check_planned_start_date(self):
|
||||
for record in self:
|
||||
if record.planned_start_date and record.planned_start_date < fields.Date.today():
|
||||
raise ValidationError("计划开工日期必须大于或等于今天。")
|
||||
|
||||
def release_production_order(self):
|
||||
def button_release_production(self):
|
||||
self.ensure_one()
|
||||
if not self.planned_start_date:
|
||||
raise ValidationError("请先填写计划开工日期")
|
||||
pro_plan_list = self.env['sf.production.plan'].search(
|
||||
[('product_id', '=', self.product_id.id), ('state', '=', 'draft')])
|
||||
pro_plan_list = self.env['sf.production.plan'].sudo().search(
|
||||
[('production_id', 'in', self.mrp_production_ids.ids), ('state', '=', 'draft')])
|
||||
sf_production_line = self.env['sf.production.line'].sudo().search(
|
||||
[('name', '=', '1#CNC自动生产线')], limit=1)
|
||||
if sf_production_line:
|
||||
@@ -342,11 +439,23 @@ class SfProductionDemandPlan(models.Model):
|
||||
pro_plan_list.production_line_id = sf_production_line.id
|
||||
pro_plan_list.date_planned_start = date_planned_start
|
||||
self._do_production_schedule(pro_plan_list)
|
||||
self.status = '60'
|
||||
self.update_sale_order_state()
|
||||
|
||||
def _do_production_schedule(self, pro_plan_list):
|
||||
for pro_plan in pro_plan_list:
|
||||
pro_plan.do_production_schedule()
|
||||
|
||||
<<<<<<< HEAD
|
||||
def update_sale_order_state(self):
|
||||
demand_plan = self.env['sf.demand.main.plan'].sudo().search([('sale_order_id', '=', self.sale_order_id.id)])
|
||||
demand_plan_state = demand_plan.filtered(lambda line: line.state != '40')
|
||||
if not demand_plan_state:
|
||||
# 修改销售订单为加工中
|
||||
self.sale_order_id.state = 'processing'
|
||||
|
||||
=======
|
||||
>>>>>>> develop
|
||||
def button_action_print(self):
|
||||
return {
|
||||
'res_model': 'sf.demand.plan.print.wizard',
|
||||
@@ -357,211 +466,481 @@ class SfProductionDemandPlan(models.Model):
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
@api.depends('sale_order_id.mrp_production_ids.state', 'sale_order_id.mrp_production_ids.programming_state')
|
||||
def _compute_hid_button(self):
|
||||
for record in self:
|
||||
mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == record.product_id.id
|
||||
)
|
||||
record.hide_action_open_mrp_production = bool(mrp_production_ids) and record.supply_method in (
|
||||
'automation', 'manual')
|
||||
programming_mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.programming_state == '编程中' and p.product_id.id == record.product_id.id
|
||||
)
|
||||
record.hide_action_view_programming = bool(programming_mrp_production_ids)
|
||||
# @api.depends('sale_order_id.mrp_production_ids.state', 'sale_order_id.mrp_production_ids.programming_state')
|
||||
# def _compute_hid_button(self):
|
||||
# for record in self:
|
||||
# mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == record.product_id.id
|
||||
# )
|
||||
# record.hide_action_open_mrp_production = bool(mrp_production_ids) and record.supply_method in (
|
||||
# 'automation', 'manual')
|
||||
# programming_mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.programming_state == '编程中' and p.product_id.id == record.product_id.id
|
||||
# )
|
||||
# record.hide_action_view_programming = bool(programming_mrp_production_ids)
|
||||
#
|
||||
# def _compute_hide_action_purchase_orders(self):
|
||||
# for record in self:
|
||||
# record.hide_action_purchase_orders = False
|
||||
# outsourcing_purchase_request = []
|
||||
# if record.supply_method in ('automation',
|
||||
# 'manual') and record.material_check == '0' and not record.sale_order_line_id.is_incoming_material:
|
||||
# mrp_production = record.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.product_id.id == record.product_id.id
|
||||
# ).sorted(key=lambda p: p.id)
|
||||
# if mrp_production:
|
||||
# raw_materials = mrp_production.mapped('move_raw_ids.product_id')
|
||||
# if raw_materials:
|
||||
# purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
# ('state', '=', 'purchase'),
|
||||
# ('order_line.product_id', 'in', raw_materials.ids)
|
||||
# ])
|
||||
# total_purchase_quantity = sum(
|
||||
# sum(
|
||||
# order.order_line.filtered(
|
||||
# lambda line: line.product_id in raw_materials
|
||||
# ).mapped('product_qty')
|
||||
# )
|
||||
# for order in purchase_orders
|
||||
# )
|
||||
# if float_compare(total_purchase_quantity, record.product_uom_qty,
|
||||
# precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
# pr_ids = self.env['purchase.request'].sudo().search(
|
||||
# [('line_ids.product_id', 'in', raw_materials.ids), ('state', '!=', 'done')])
|
||||
# outsourcing_purchase_request.extend(pr_ids.ids)
|
||||
# elif record.supply_method in ('purchase', 'outsourcing'):
|
||||
# purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
# ('state', 'in', ('purchase', 'done')),
|
||||
# ('order_line.product_id', '=', record.product_id.id)
|
||||
# ])
|
||||
# total_purchase_quantity = sum(
|
||||
# sum(
|
||||
# order.order_line.filtered(
|
||||
# lambda line: line.product_id in record.product_id
|
||||
# ).mapped('product_qty')
|
||||
# )
|
||||
# for order in purchase_orders
|
||||
# )
|
||||
#
|
||||
# if float_compare(total_purchase_quantity, record.product_uom_qty,
|
||||
# precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
# pr_ids = self.env['purchase.request'].sudo().search(
|
||||
# [('origin', 'like', record.sale_order_id.name), ('state', '!=', 'done')])
|
||||
# outsourcing_purchase_request.extend(pr_ids.ids)
|
||||
# if record.supply_method == 'outsourcing' and not record.sale_order_line_id.is_incoming_material:
|
||||
# bom_line_ids = record.product_id.bom_ids.bom_line_ids
|
||||
# if bom_line_ids:
|
||||
# # BOM_数量
|
||||
# total_product_qty = sum(line.product_qty for line in bom_line_ids)
|
||||
# bom_product_ids = bom_line_ids.mapped('product_id')
|
||||
# product_purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
# ('state', 'in', ('purchase', 'done')),
|
||||
# ('order_line.product_id', 'in', bom_product_ids.ids)
|
||||
# ])
|
||||
# # 购订单_数量
|
||||
# total_outsourcing_purchase_quantity = sum(
|
||||
# sum(
|
||||
# order.order_line.filtered(
|
||||
# lambda line: line.product_id in bom_product_ids
|
||||
# ).mapped('product_qty')
|
||||
# )
|
||||
# for order in product_purchase_orders
|
||||
# )
|
||||
# quantity = total_outsourcing_purchase_quantity / total_product_qty
|
||||
# if float_compare(quantity, record.product_uom_qty,
|
||||
# precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
# purchase_request = self.env['purchase.request'].sudo().search(
|
||||
# [('line_ids.product_id', 'in', bom_product_ids.ids),
|
||||
# ('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')])
|
||||
# outsourcing_purchase_request.extend(purchase_request.ids)
|
||||
# record.outsourcing_purchase_request = json.dumps(outsourcing_purchase_request)
|
||||
# if outsourcing_purchase_request:
|
||||
# record.hide_action_purchase_orders = True
|
||||
#
|
||||
# @api.depends('sale_order_id.mrp_production_ids.picking_ids.state', 'sale_order_id.picking_ids.state')
|
||||
# def _compute_hide_action_stock_picking(self):
|
||||
# for record in self:
|
||||
# record.hide_action_stock_picking = False
|
||||
# record.hide_action_outsourcing_stock_picking = False
|
||||
# if record.supply_method in ('automation', 'manual'):
|
||||
# manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.product_id.id == record.product_id.id
|
||||
# )
|
||||
# record.hide_action_stock_picking = bool(manufacturing_orders.mapped('picking_ids').filtered(
|
||||
# lambda p: p.state == 'assigned'))
|
||||
# elif record.supply_method in ('purchase', 'outsourcing'):
|
||||
# assigned_picking_ids = record.sale_order_id.picking_ids.filtered(
|
||||
# lambda
|
||||
# p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in record.product_id)
|
||||
# if record.supply_method == 'outsourcing':
|
||||
# outsourcing_assigned_picking_ids = record.get_outsourcing_picking_ids()
|
||||
# record.hide_action_outsourcing_stock_picking = outsourcing_assigned_picking_ids
|
||||
# record.hide_action_stock_picking = assigned_picking_ids or outsourcing_assigned_picking_ids
|
||||
# else:
|
||||
# record.hide_action_stock_picking = assigned_picking_ids
|
||||
#
|
||||
# def get_outsourcing_picking_ids(self):
|
||||
# order_ids = self.env['purchase.order'].sudo().search(
|
||||
# [('order_line.product_id', 'in', self.product_id.ids),
|
||||
# ('purchase_type', '=', 'outsourcing')])
|
||||
# outsourcing_picking_ids = order_ids._get_subcontracting_resupplies()
|
||||
# outsourcing_assigned_picking_ids = outsourcing_picking_ids.filtered(lambda p: p.state == 'assigned')
|
||||
# return outsourcing_assigned_picking_ids
|
||||
|
||||
def _compute_hide_action_purchase_orders(self):
|
||||
for record in self:
|
||||
record.hide_action_purchase_orders = False
|
||||
outsourcing_purchase_request = []
|
||||
if record.supply_method in ('automation',
|
||||
'manual') and record.material_check == '0' and not record.sale_order_line_id.is_incoming_material:
|
||||
mrp_production = record.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.product_id.id == record.product_id.id
|
||||
).sorted(key=lambda p: p.id)
|
||||
if mrp_production:
|
||||
raw_materials = mrp_production.mapped('move_raw_ids.product_id')
|
||||
if raw_materials:
|
||||
purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
('state', '=', 'purchase'),
|
||||
('order_line.product_id', 'in', raw_materials.ids)
|
||||
])
|
||||
total_purchase_quantity = sum(
|
||||
sum(
|
||||
order.order_line.filtered(
|
||||
lambda line: line.product_id in raw_materials
|
||||
).mapped('product_qty')
|
||||
)
|
||||
for order in purchase_orders
|
||||
)
|
||||
if float_compare(total_purchase_quantity, record.product_uom_qty,
|
||||
precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
pr_ids = self.env['purchase.request'].sudo().search(
|
||||
[('line_ids.product_id', 'in', raw_materials.ids), ('state', '!=', 'done')])
|
||||
outsourcing_purchase_request.extend(pr_ids.ids)
|
||||
elif record.supply_method in ('purchase', 'outsourcing'):
|
||||
purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
('state', 'in', ('purchase', 'done')),
|
||||
('order_line.product_id', '=', record.product_id.id)
|
||||
])
|
||||
total_purchase_quantity = sum(
|
||||
sum(
|
||||
order.order_line.filtered(
|
||||
lambda line: line.product_id in record.product_id
|
||||
).mapped('product_qty')
|
||||
)
|
||||
for order in purchase_orders
|
||||
)
|
||||
# def action_open_sale_order(self):
|
||||
# self.ensure_one()
|
||||
# return {
|
||||
# 'type': 'ir.actions.act_window',
|
||||
# 'res_model': 'sale.order',
|
||||
# 'res_id': self.sale_order_id.id,
|
||||
# 'view_mode': 'form',
|
||||
# }
|
||||
|
||||
if float_compare(total_purchase_quantity, record.product_uom_qty,
|
||||
precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
pr_ids = self.env['purchase.request'].sudo().search(
|
||||
[('origin', 'like', record.sale_order_id.name), ('state', '!=', 'done')])
|
||||
outsourcing_purchase_request.extend(pr_ids.ids)
|
||||
if record.supply_method == 'outsourcing' and not record.sale_order_line_id.is_incoming_material:
|
||||
bom_line_ids = record.product_id.bom_ids.bom_line_ids
|
||||
if bom_line_ids:
|
||||
# BOM_数量
|
||||
total_product_qty = sum(line.product_qty for line in bom_line_ids)
|
||||
bom_product_ids = bom_line_ids.mapped('product_id')
|
||||
product_purchase_orders = self.env['purchase.order'].sudo().search([
|
||||
('state', 'in', ('purchase', 'done')),
|
||||
('order_line.product_id', 'in', bom_product_ids.ids)
|
||||
])
|
||||
# 购订单_数量
|
||||
total_outsourcing_purchase_quantity = sum(
|
||||
sum(
|
||||
order.order_line.filtered(
|
||||
lambda line: line.product_id in bom_product_ids
|
||||
).mapped('product_qty')
|
||||
)
|
||||
for order in product_purchase_orders
|
||||
)
|
||||
quantity = total_outsourcing_purchase_quantity / total_product_qty
|
||||
if float_compare(quantity, record.product_uom_qty,
|
||||
precision_rounding=record.product_id.uom_id.rounding) == -1:
|
||||
purchase_request = self.env['purchase.request'].sudo().search(
|
||||
[('line_ids.product_id', 'in', bom_product_ids.ids),
|
||||
('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')])
|
||||
outsourcing_purchase_request.extend(purchase_request.ids)
|
||||
record.outsourcing_purchase_request = json.dumps(outsourcing_purchase_request)
|
||||
if outsourcing_purchase_request:
|
||||
record.hide_action_purchase_orders = True
|
||||
# def action_open_mrp_production(self):
|
||||
# self.ensure_one()
|
||||
# mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == self.product_id.id
|
||||
# )
|
||||
# action = {
|
||||
# 'res_model': 'mrp.production',
|
||||
# 'type': 'ir.actions.act_window',
|
||||
# }
|
||||
# if len(mrp_production_ids) == 1:
|
||||
# action.update({
|
||||
# 'view_mode': 'form',
|
||||
# 'res_id': mrp_production_ids.id,
|
||||
# })
|
||||
# else:
|
||||
# action.update({
|
||||
# 'name': _("制造订单列表"),
|
||||
# 'domain': [('id', 'in', mrp_production_ids.ids)],
|
||||
# 'view_mode': 'tree,form',
|
||||
# })
|
||||
# return action
|
||||
|
||||
@api.depends('sale_order_id.mrp_production_ids.picking_ids.state', 'sale_order_id.picking_ids.state')
|
||||
def _compute_hide_action_stock_picking(self):
|
||||
for record in self:
|
||||
record.hide_action_stock_picking = False
|
||||
record.hide_action_outsourcing_stock_picking = False
|
||||
if record.supply_method in ('automation', 'manual'):
|
||||
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.product_id.id == record.product_id.id
|
||||
)
|
||||
record.hide_action_stock_picking = bool(manufacturing_orders.mapped('picking_ids').filtered(
|
||||
lambda p: p.state == 'assigned'))
|
||||
elif record.supply_method in ('purchase', 'outsourcing'):
|
||||
assigned_picking_ids = record.sale_order_id.picking_ids.filtered(
|
||||
lambda
|
||||
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in record.product_id)
|
||||
if record.supply_method == 'outsourcing':
|
||||
outsourcing_assigned_picking_ids = record.get_outsourcing_picking_ids()
|
||||
record.hide_action_outsourcing_stock_picking = outsourcing_assigned_picking_ids
|
||||
record.hide_action_stock_picking = assigned_picking_ids or outsourcing_assigned_picking_ids
|
||||
else:
|
||||
record.hide_action_stock_picking = assigned_picking_ids
|
||||
# def action_view_purchase_request(self):
|
||||
# self.ensure_one()
|
||||
# pr_ids = self.env['purchase.request'].sudo().search(
|
||||
# [('id', 'in', ast.literal_eval(self.outsourcing_purchase_request))])
|
||||
# action = {
|
||||
# 'res_model': 'purchase.request',
|
||||
# 'type': 'ir.actions.act_window',
|
||||
# }
|
||||
# if len(pr_ids) == 1:
|
||||
# action.update({
|
||||
# 'view_mode': 'form',
|
||||
# 'res_id': pr_ids[0].id,
|
||||
# })
|
||||
# else:
|
||||
# action.update({
|
||||
# 'name': _("采购申请"),
|
||||
# 'domain': [('id', 'in', pr_ids.ids)],
|
||||
# 'view_mode': 'tree,form',
|
||||
# })
|
||||
# return action
|
||||
|
||||
def get_outsourcing_picking_ids(self):
|
||||
order_ids = self.env['purchase.order'].sudo().search(
|
||||
[('order_line.product_id', 'in', self.product_id.ids),
|
||||
('purchase_type', '=', 'outsourcing')])
|
||||
outsourcing_picking_ids = order_ids._get_subcontracting_resupplies()
|
||||
outsourcing_assigned_picking_ids = outsourcing_picking_ids.filtered(lambda p: p.state == 'assigned')
|
||||
return outsourcing_assigned_picking_ids
|
||||
# def action_view_stock_picking(self):
|
||||
# self.ensure_one()
|
||||
# action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
|
||||
# picking_ids = None
|
||||
# if self.supply_method in ('automation', 'manual'):
|
||||
# mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.product_id.id == self.product_id.id
|
||||
# )
|
||||
# picking_ids = mrp_production_ids.mapped('picking_ids').filtered(
|
||||
# lambda p: p.state == 'assigned')
|
||||
# elif self.supply_method in ('purchase', 'outsourcing'):
|
||||
# picking_ids = self.sale_order_id.picking_ids.filtered(
|
||||
# lambda
|
||||
# p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in self.product_id)
|
||||
# if self.supply_method == 'outsourcing' and self.hide_action_outsourcing_stock_picking:
|
||||
# picking_ids = picking_ids.union(self.get_outsourcing_picking_ids())
|
||||
# if picking_ids:
|
||||
# if len(picking_ids) > 1:
|
||||
# action['domain'] = [('id', 'in', picking_ids.ids)]
|
||||
# elif picking_ids:
|
||||
# action['res_id'] = picking_ids.id
|
||||
# action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
|
||||
# if 'views' in action:
|
||||
# action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
|
||||
# return action
|
||||
|
||||
def action_open_sale_order(self):
|
||||
# def action_view_programming(self):
|
||||
# self.ensure_one()
|
||||
# programming_mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
# lambda p: p.programming_state == '编程中' and p.product_id.id == self.product_id.id
|
||||
# ).mapped('programming_no')
|
||||
# if programming_mrp_production_ids:
|
||||
# programming_no = list(set(programming_mrp_production_ids))
|
||||
# numbers_str = "、".join(programming_no)
|
||||
# raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
|
||||
|
||||
def action_edit_demand_plan_form(self):
|
||||
"""编辑需求计划详细信息"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
action = {
|
||||
'name': _("需求计划"),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'sale.order',
|
||||
'res_id': self.sale_order_id.id,
|
||||
'view_mode': 'form',
|
||||
'views': [[self.env.ref('sf_demand_plan.view_sf_demand_main_plan_form').id, 'form']],
|
||||
'res_model': 'sf.demand.main.plan',
|
||||
'res_id': self.demand_plan_id.id,
|
||||
}
|
||||
|
||||
def action_open_mrp_production(self):
|
||||
#打开需求计划视图时,如果状态时已下达,则只读,不能添加明细了
|
||||
if self.demand_plan_id.state == '40':
|
||||
action.update({'flags': {'mode': 'readonly'}})
|
||||
return action
|
||||
#删除需求计划
|
||||
def button_delete(self):
|
||||
self.ensure_one()
|
||||
mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == self.product_id.id
|
||||
if len(self.demand_plan_id.line_ids) == 1:
|
||||
raise ValidationError(f"最后一条计划,不能删除!")
|
||||
self.unlink()
|
||||
#需求明细行里下达需求计划
|
||||
def button_release_plan(self):
|
||||
self.ensure_one()
|
||||
if not self.supply_method:
|
||||
raise ValidationError(f"供货方式不能为空!")
|
||||
#点击时校验,累计计划量>需求数量,则提示,新增了个提示确认视图
|
||||
if self.plan_uom_qty > self.product_uom_qty:
|
||||
return {
|
||||
'name': _('需求计划'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'views': [(self.env.ref('sf_demand_plan.sf_release_plan_wizard_form').id,'form')],
|
||||
'res_model': 'sf.release.plan.wizard',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_demand_plan_line_id': self.id,
|
||||
'default_release_message': f"您正在下达计划量 {self.plan_uom_qty},累计计划量为 {self.demand_plan_id.planned_qty},需求数量为 {self.product_uom_qty},已超过需求数量,是否继续?",
|
||||
}}
|
||||
self.action_confirm()
|
||||
|
||||
def action_confirm(self):
|
||||
self = self.with_context(
|
||||
demand_plan_line_id=self.id
|
||||
)
|
||||
action = {
|
||||
'res_model': 'mrp.production',
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
if len(mrp_production_ids) == 1:
|
||||
action.update({
|
||||
'view_mode': 'form',
|
||||
'res_id': mrp_production_ids.id,
|
||||
})
|
||||
else:
|
||||
action.update({
|
||||
'name': _("制造订单列表"),
|
||||
'domain': [('id', 'in', mrp_production_ids.ids)],
|
||||
'view_mode': 'tree,form',
|
||||
})
|
||||
return action
|
||||
|
||||
def action_view_purchase_request(self):
|
||||
self.ensure_one()
|
||||
pr_ids = self.env['purchase.request'].sudo().search(
|
||||
[('id', 'in', ast.literal_eval(self.outsourcing_purchase_request))])
|
||||
action = {
|
||||
'res_model': 'purchase.request',
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
if len(pr_ids) == 1:
|
||||
action.update({
|
||||
'view_mode': 'form',
|
||||
'res_id': pr_ids[0].id,
|
||||
})
|
||||
else:
|
||||
action.update({
|
||||
'name': _("采购申请"),
|
||||
'domain': [('id', 'in', pr_ids.ids)],
|
||||
'view_mode': 'tree,form',
|
||||
})
|
||||
return action
|
||||
|
||||
def action_view_stock_picking(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
|
||||
picking_ids = None
|
||||
self.mrp_bom_create()
|
||||
self._action_launch_stock_rule()
|
||||
#自动化产线,人工产线,待下达生产
|
||||
if self.supply_method in ('automation', 'manual'):
|
||||
mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.product_id.id == self.product_id.id
|
||||
)
|
||||
picking_ids = mrp_production_ids.mapped('picking_ids').filtered(
|
||||
lambda p: p.state == 'assigned')
|
||||
elif self.supply_method in ('purchase', 'outsourcing'):
|
||||
picking_ids = self.sale_order_id.picking_ids.filtered(
|
||||
lambda
|
||||
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in self.product_id)
|
||||
if self.supply_method == 'outsourcing' and self.hide_action_outsourcing_stock_picking:
|
||||
picking_ids = picking_ids.union(self.get_outsourcing_picking_ids())
|
||||
if picking_ids:
|
||||
if len(picking_ids) > 1:
|
||||
action['domain'] = [('id', 'in', picking_ids.ids)]
|
||||
elif picking_ids:
|
||||
action['res_id'] = picking_ids.id
|
||||
action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
|
||||
if 'views' in action:
|
||||
action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
|
||||
return action
|
||||
self.write({'status': '50'})
|
||||
else:
|
||||
#外购,委外,已下达
|
||||
self.write({'status': '60'})
|
||||
self.update_sale_order_state()
|
||||
|
||||
def action_view_programming(self):
|
||||
def mrp_bom_create(self):
|
||||
# 检查是否已有相同供货方式的BOM
|
||||
if self.supply_method in ('automation', 'manual'):
|
||||
#同一需求计划下,相同供货方式的产品可以共享BOM
|
||||
line_ids = self.demand_plan_id.line_ids.filtered(
|
||||
lambda p: p.supply_method in ('automation', 'manual') and p.status in ('50', '60'))
|
||||
if line_ids:
|
||||
self.bom_id = line_ids[0].bom_id.id # 复用已有BOM
|
||||
return
|
||||
elif self.supply_method == 'outsourcing':
|
||||
line_ids = self.demand_plan_id.line_ids.filtered(
|
||||
lambda p: p.supply_method == 'outsourcing' and p.status == '60')
|
||||
if line_ids:
|
||||
self.bom_id = line_ids[0].bom_id.id
|
||||
return
|
||||
bom_type = ''
|
||||
# 根据供货方式修改成品模板
|
||||
if self.supply_method == 'automation':
|
||||
bom_type = 'normal'
|
||||
product_template_id = self.env.ref('sf_dlm.product_template_sf').sudo().product_tmpl_id
|
||||
elif self.supply_method == 'outsourcing':
|
||||
bom_type = 'subcontract'
|
||||
product_template_id = self.env.ref(
|
||||
'jikimo_sale_multiple_supply_methods.product_template_outsourcing').sudo()
|
||||
elif self.supply_method == 'purchase':
|
||||
product_template_id = self.env.ref(
|
||||
'jikimo_sale_multiple_supply_methods.product_template_purchase').sudo()
|
||||
elif self.supply_method == 'manual':
|
||||
bom_type = 'normal'
|
||||
product_template_id = self.env.ref(
|
||||
'jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo()
|
||||
|
||||
# 复制成品模板上的属性
|
||||
self.product_id.product_tmpl_id.copy_template(product_template_id)
|
||||
|
||||
# 生成BOM单据编码
|
||||
code = f"{self.product_id.default_code}-{bom_type}-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
|
||||
order_id = self.sale_order_id
|
||||
product = self.product_id
|
||||
# 拼接方法需要的item结构,成品的模型数据信息就是坯料的数据信息
|
||||
item = {
|
||||
'texture_code': product.materials_id.materials_no,
|
||||
'texture_type_code': product.materials_type_id.materials_no,
|
||||
'model_long': product.length,
|
||||
'model_width': product.width,
|
||||
'model_height': product.height,
|
||||
'blank_volume': product.model_volume,
|
||||
'blank_area': product.model_area,
|
||||
'price': product.list_price,
|
||||
'embryo_redundancy_id': self.sale_order_line_id.embryo_redundancy_id,
|
||||
'model_id': self.model_id
|
||||
}
|
||||
product_name = ''
|
||||
match = re.search(r'(S\d{5}-\d+)', product.name)
|
||||
product_seria = 0
|
||||
# 如果匹配成功,提取结果
|
||||
if match:
|
||||
product_name = match.group(0)
|
||||
# 获取成品名结尾-n的n
|
||||
product_seria = int(product_name.split('-')[-1])
|
||||
|
||||
# 成品供货方式为采购则不生成bom
|
||||
if self.supply_method != 'purchase':
|
||||
# 当成品上带有客供料选项时,生成坯料时选择“客供料”路线
|
||||
if self.sale_order_line_id.embryo_redundancy_id:
|
||||
# 将成品模板的内容复制到成品上
|
||||
customer_provided_embryo = self.env.ref(
|
||||
'jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo()
|
||||
# 创建坯料,客供料的批量不需要创建bom
|
||||
material_customer_provided_embryo = self.env['product.template'].sudo().no_bom_product_create(
|
||||
customer_provided_embryo.with_context(active_test=False).product_variant_id,
|
||||
item,
|
||||
order_id, 'material_customer_provided', product_seria, product)
|
||||
# 成品配置bom
|
||||
product_bom_material_customer_provided = self.env['mrp.bom'].with_user(
|
||||
self.env.ref("base.user_admin")).bom_create(
|
||||
product, bom_type, 'product', code)
|
||||
product_bom_material_customer_provided.with_user(
|
||||
self.env.ref("base.user_admin")).bom_create_line_has(
|
||||
material_customer_provided_embryo)
|
||||
self.bom_id = product_bom_material_customer_provided.id
|
||||
elif self.product_id.materials_type_id.gain_way == '自加工':
|
||||
self_machining_id = self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo()
|
||||
# 创建坯料
|
||||
self_machining_embryo = self.env['product.template'].sudo().no_bom_product_create(
|
||||
self_machining_id,
|
||||
item,
|
||||
order_id, 'self_machining', product_seria, product)
|
||||
# 创建坯料的bom
|
||||
self_machining_bom = self.env['mrp.bom'].with_user(
|
||||
self.env.ref("base.user_admin")).bom_create(
|
||||
self_machining_embryo, 'normal', False)
|
||||
# 创建坯料里bom的组件
|
||||
self_machining_bom_line = self_machining_bom.with_user(
|
||||
self.env.ref("base.user_admin")).bom_create_line(
|
||||
self_machining_embryo)
|
||||
if not self_machining_bom_line:
|
||||
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
|
||||
# 产品配置bom
|
||||
product_bom_self_machining = self.env['mrp.bom'].with_user(
|
||||
self.env.ref("base.user_admin")).bom_create(
|
||||
product, bom_type, 'product', code)
|
||||
product_bom_self_machining.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||
self_machining_embryo)
|
||||
self.bom_id = product_bom_self_machining.id
|
||||
elif self.product_id.materials_type_id.gain_way == '外协':
|
||||
outsource_id = self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo()
|
||||
# 创建坯料
|
||||
outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id,
|
||||
item,
|
||||
order_id,
|
||||
'subcontract',
|
||||
product_seria,
|
||||
product)
|
||||
if outsource_embryo == -3:
|
||||
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
|
||||
# 创建坯料的bom
|
||||
outsource_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create(
|
||||
outsource_embryo,
|
||||
'subcontract', True)
|
||||
# 创建坯料的bom的组件
|
||||
outsource_bom_line = outsource_bom.with_user(
|
||||
self.env.ref("base.user_admin")).bom_create_line(outsource_embryo)
|
||||
if not outsource_bom_line:
|
||||
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
|
||||
# 产品配置bom
|
||||
product_bom_outsource = self.env['mrp.bom'].with_user(
|
||||
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product', code)
|
||||
product_bom_outsource.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||
outsource_embryo)
|
||||
self.bom_id = product_bom_outsource.id
|
||||
elif self.product_id.materials_type_id.gain_way == '采购':
|
||||
purchase_id = self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo()
|
||||
purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id,item,order_id,'purchase',product_seria,product)
|
||||
if purchase_embryo and purchase_embryo == -3:
|
||||
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
|
||||
else:
|
||||
# 产品配置bom
|
||||
product_bom_purchase = self.env['mrp.bom'].with_user(
|
||||
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product', code)
|
||||
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||
purchase_embryo)
|
||||
self.bom_id = product_bom_purchase.id
|
||||
|
||||
#以下方法待了解
|
||||
def _action_launch_stock_rule(self):
|
||||
procurements = []
|
||||
#创建采购需求
|
||||
group_id = self.sale_order_id.procurement_group_id
|
||||
if not group_id:
|
||||
group_id = self.env['procurement.group'].create(self._prepare_procurement_group_vals())
|
||||
self.sale_order_id.procurement_group_id = group_id
|
||||
else:
|
||||
updated_vals = {}
|
||||
if group_id.partner_id != self.sale_order_id.partner_shipping_id:
|
||||
updated_vals.update({'partner_id': self.sale_order_id.partner_shipping_id.id})
|
||||
if group_id.move_type != self.sale_order_id.picking_policy:
|
||||
updated_vals.update({'move_type': self.sale_order_id.picking_policy})
|
||||
if updated_vals:
|
||||
group_id.write(updated_vals)
|
||||
values = self._prepare_procurement_values(group_id=group_id)
|
||||
line_uom = self.sale_order_line_id.product_uom
|
||||
quant_uom = self.product_id.uom_id
|
||||
plan_uom_qty, procurement_uom = line_uom._adjust_uom_quantities(self.plan_uom_qty, quant_uom)
|
||||
#运行采购规则
|
||||
procurements.append(self.env['procurement.group'].Procurement(
|
||||
self.product_id, plan_uom_qty, procurement_uom,
|
||||
self.sale_order_id.partner_shipping_id.property_stock_customer,
|
||||
self.product_id.display_name, self.sale_order_id.name, self.sale_order_id.company_id, values))
|
||||
if procurements:
|
||||
procurement_group = self.env['procurement.group']
|
||||
if self.env.context.get('import_file'):
|
||||
procurement_group = procurement_group.with_context(import_file=False)
|
||||
#确认拣货单
|
||||
procurement_group.run(procurements)
|
||||
orders = self.mapped('sale_order_id')
|
||||
for order in orders:
|
||||
pickings_to_confirm = order.picking_ids.filtered(lambda p: p.state not in ['cancel', 'done'])
|
||||
if pickings_to_confirm:
|
||||
pickings_to_confirm.action_confirm()
|
||||
return True
|
||||
|
||||
def _prepare_procurement_group_vals(self):
|
||||
return {
|
||||
'name': self.sale_order_id.name,
|
||||
'move_type': self.sale_order_id.picking_policy,
|
||||
'sale_id': self.sale_order_id.id,
|
||||
'partner_id': self.sale_order_id.partner_shipping_id.id,
|
||||
}
|
||||
|
||||
def _prepare_procurement_values(self, group_id=False):
|
||||
self.ensure_one()
|
||||
programming_mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||
lambda p: p.programming_state == '编程中' and p.product_id.id == self.product_id.id
|
||||
).mapped('programming_no')
|
||||
if programming_mrp_production_ids:
|
||||
programming_no = list(set(programming_mrp_production_ids))
|
||||
numbers_str = "、".join(programming_no)
|
||||
raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
|
||||
date_deadline = self.sale_order_id.commitment_date or (
|
||||
self.sale_order_id.date_order + timedelta(days=self.sale_order_line_id.customer_lead or 0.0))
|
||||
date_planned = date_deadline - timedelta(days=self.sale_order_id.company_id.security_lead)
|
||||
values = {
|
||||
'group_id': group_id,
|
||||
'sale_line_id': self.sale_order_line_id.id,
|
||||
'date_planned': date_planned,
|
||||
'date_deadline': date_deadline,
|
||||
'route_ids': self.route_ids,
|
||||
'warehouse_id': self.sale_order_id.warehouse_id or False,
|
||||
'partner_id': self.sale_order_id.partner_shipping_id.id,
|
||||
'product_description_variants': self.sale_order_line_id.with_context(
|
||||
lang=self.sale_order_id.partner_id.lang)._get_sale_order_line_multiline_description_variants(),
|
||||
'company_id': self.sale_order_id.company_id,
|
||||
'product_packaging_id': self.sale_order_line_id.product_packaging_id,
|
||||
'sequence': self.sale_order_line_id.sequence,
|
||||
'demand_plan_line_id': self.id
|
||||
}
|
||||
return values
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@ class SfStockRoute(models.Model):
|
||||
[('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):
|
||||
|
||||
22
sf_demand_plan/models/stock_rule.py
Normal file
22
sf_demand_plan/models/stock_rule.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- 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
|
||||
@@ -1,9 +1,20 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_sf_production_demand_plan,sf.production.demand.plan,model_sf_production_demand_plan,base.group_user,1,0,0,0
|
||||
access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for dispatch,model_sf_production_demand_plan,sf_base.group_plan_dispatch,1,1,0,0
|
||||
access_sf_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_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
|
||||
|
||||
<<<<<<< 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
|
||||
|
||||
|
@@ -81,4 +81,9 @@
|
||||
input,label {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th[data-name=processing_time] + th::before{
|
||||
content: '待执行单据';
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
70
sf_demand_plan/views/demand_main_plan.xml
Normal file
70
sf_demand_plan/views/demand_main_plan.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<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>
|
||||
@@ -1,17 +1,16 @@
|
||||
<odoo>
|
||||
<odoo>
|
||||
<record id="view_sf_production_demand_plan_tree" model="ir.ui.view">
|
||||
<field name="name">sf.production.demand.plan.tree</field>
|
||||
<field name="model">sf.production.demand.plan</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="需求计划" default_order="sequence desc,id desc" editable="bottom"
|
||||
class="demand_plan_tree">
|
||||
class="demand_plan_tree freeze-columns-before-part_number" create="false" delete="false">
|
||||
<header>
|
||||
<button string="打印" name="button_action_print" type="object"
|
||||
class="btn-primary"/>
|
||||
</header>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="id" optional="hide"/>
|
||||
<field name="priority"/>
|
||||
<field name="status"/>
|
||||
<field name="customer_name"/>
|
||||
<field name="order_remark"/>
|
||||
@@ -21,7 +20,7 @@
|
||||
<field name="part_name"/>
|
||||
<field name="part_number"/>
|
||||
<field name="is_incoming_material"/>
|
||||
<field name="supply_method"/>
|
||||
<field name="supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="deadline_of_delivery"/>
|
||||
<field name="inventory_quantity_auto_apply"/>
|
||||
@@ -29,47 +28,50 @@
|
||||
<field name="qty_to_deliver"/>
|
||||
<field name="model_long"/>
|
||||
<field name="blank_type" optional="hide"/>
|
||||
<field name="blank_precision"/>
|
||||
<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="order_code" optional="hide"/>
|
||||
<field name="sale_order_id" optional="hide"/>
|
||||
<field name="sale_order_line_number" optional="hide"/>
|
||||
<field name="order_state"/>
|
||||
<field name="route_ids" widget="many2many_tags" optional="hide" readonly="0"
|
||||
context="{'demand_plan_search_stock_route_id': id}"/>
|
||||
<field name="contract_date"/>
|
||||
<field name="date_order"/>
|
||||
<field name="contract_code"/>
|
||||
<field name="plan_remark"/>
|
||||
<field name="processing_time"/>
|
||||
<field name="plan_remark" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/>
|
||||
<field name="priority" decoration-danger="priority == '1'"
|
||||
decoration-warning="priority == '2'"
|
||||
decoration-info="priority == '3'"
|
||||
decoration-success="priority == '4'"/>
|
||||
<field name="material_check" optional="hide"/>
|
||||
<field name="hide_action_open_mrp_production" invisible="1"/>
|
||||
<field name="hide_action_purchase_orders" invisible="1"/>
|
||||
<field name="hide_action_stock_picking" invisible="1"/>
|
||||
<field name="hide_action_view_programming" invisible="1"/>
|
||||
<button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"
|
||||
attrs="{'invisible': [('supply_method', '!=', False)]}"/>
|
||||
<button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"
|
||||
attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>
|
||||
<button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"
|
||||
attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>
|
||||
<button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"
|
||||
attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>
|
||||
<button name="action_view_programming" type="object" string="编程单" class="btn-secondary"
|
||||
attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>
|
||||
<field name="planned_start_date"/>
|
||||
<!-- <field name="hide_action_open_mrp_production" invisible="1"/>-->
|
||||
<!-- <field name="hide_action_purchase_orders" invisible="1"/>-->
|
||||
<!-- <field name="hide_action_stock_picking" invisible="1"/>-->
|
||||
<!-- <field name="hide_action_view_programming" invisible="1"/>-->
|
||||
<!-- <button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"-->
|
||||
<!-- attrs="{'invisible': [('supply_method', '!=', False)]}"/>-->
|
||||
<!-- <button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"-->
|
||||
<!-- attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>-->
|
||||
<!-- <button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"-->
|
||||
<!-- attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>-->
|
||||
<!-- <button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"-->
|
||||
<!-- attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>-->
|
||||
<!-- <button name="action_view_programming" type="object" string="编程单" class="btn-secondary"-->
|
||||
<!-- attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>-->
|
||||
<field name="planned_start_date" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/>
|
||||
<field name="actual_start_date"/>
|
||||
<field name="actual_end_date"/>
|
||||
<field name="processing_time"/>
|
||||
<field name="create_date" optional="hide" string="创建时间"/>
|
||||
<field name="create_uid" optional="hide" string="创建人"/>
|
||||
<field name="write_date" string="更新时间"/>
|
||||
<field name="write_uid" optional="hide" string="更新人"/>
|
||||
<field name="print_count"/>
|
||||
<button name="release_production_order" type="object" string="下达生产" class="btn-primary"
|
||||
attrs="{'invisible': ['|',('status', '!=', '50'), ('supply_method', 'not in', ['automation', 'manual'])]}"/>
|
||||
<field name="hide_release_production_order" invisible="1"/>
|
||||
<button name="button_release_production" type="object" string="下发生产" class="btn-primary"
|
||||
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
|
||||
/>
|
||||
<button name="action_edit_demand_plan_form" type="object" string="编辑" class="btn-primary"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -92,7 +94,8 @@
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/>
|
||||
<filter name="group_by_status" string="状态" domain="[]" context="{'group_by': 'status'}"/>
|
||||
<filter name="group_by_customer_name" string="客户" domain="[]" context="{'group_by': 'customer_name'}"/>
|
||||
<filter name="group_by_customer_name" string="客户" domain="[]"
|
||||
context="{'group_by': 'customer_name'}"/>
|
||||
<filter name="group_by_is_incoming_material" string="客供料" domain="[]"
|
||||
context="{'group_by': 'is_incoming_material'}"/>
|
||||
<filter name="group_by_supply_method" string="供货方式" domain="[]"
|
||||
@@ -106,8 +109,7 @@
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</record>
|
||||
<record id="sf_production_demand_plan_action" model="ir.actions.act_window">
|
||||
<field name="name">需求计划</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -115,12 +117,4 @@
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem
|
||||
id="demand_plan_menu"
|
||||
name="需求计划"
|
||||
sequence="140"
|
||||
action="sf_production_demand_plan_action"
|
||||
parent="sf_plan.sf_production_plan_menu"
|
||||
/>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
||||
18
sf_demand_plan/views/menu_view.xml
Normal file
18
sf_demand_plan/views/menu_view.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>
|
||||
29
sf_demand_plan/views/sale_order_views.xml
Normal file
29
sf_demand_plan/views/sale_order_views.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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>
|
||||
@@ -1 +1,2 @@
|
||||
from . import sf_demand_plan_print_wizard
|
||||
from . import sf_release_plan_wizard
|
||||
22
sf_demand_plan/wizard/sf_release_plan_wizard.py
Normal file
22
sf_demand_plan/wizard/sf_release_plan_wizard.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- 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()
|
||||
22
sf_demand_plan/wizard/sf_release_plan_wizard_views.xml
Normal file
22
sf_demand_plan/wizard/sf_release_plan_wizard_views.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
|
||||
import re
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ResProductCategory(models.Model):
|
||||
@@ -47,11 +48,14 @@ class ResMrpBomMo(models.Model):
|
||||
item.subcontractor_name = ''
|
||||
|
||||
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 = {
|
||||
'bom_id': self.id,
|
||||
'product_id': embryo.id,
|
||||
'product_tmpl_id': embryo.product_tmpl_id.id,
|
||||
'product_qty': 1,
|
||||
'product_qty': product.unit_number,
|
||||
'product_uom_id': 1
|
||||
}
|
||||
return self.env['mrp.bom.line'].sudo().create(vals)
|
||||
@@ -122,7 +126,7 @@ class ResMrpBomMo(models.Model):
|
||||
# 查bom的原材料
|
||||
def get_raw_bom(self, product):
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
<page string="加工参数">
|
||||
<group>
|
||||
<group string="模型">
|
||||
<field name="blank_type" readonly="1"/>
|
||||
<field name="blank_precision" readonly="1"/>
|
||||
<label for="model_long" string="坯料尺寸[mm]"/>
|
||||
<div class="o_address_format">
|
||||
<label for="model_long" string="长"/>
|
||||
@@ -104,7 +106,7 @@
|
||||
<label for="model_height" string="高"/>
|
||||
<field name="model_height" class="o_address_zip"/>
|
||||
</div>
|
||||
<field name="blank_type" readonly="1"/>
|
||||
<field name="unit_number" readonly="1"/>
|
||||
<field name="model_volume" string="体积[mm³]"/>
|
||||
<field name="product_model_type_id" string="模型类型"/>
|
||||
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"
|
||||
|
||||
@@ -27,13 +27,15 @@ class ResProductMo(models.Model):
|
||||
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
|
||||
model_name = fields.Char('模型名称')
|
||||
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类')
|
||||
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型')
|
||||
model_long = fields.Float('模型长(mm)', digits=(16, 3))
|
||||
model_width = 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_area = fields.Float('模型表面积(m²)')
|
||||
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
|
||||
model_processing_panel = fields.Char('模型加工面板')
|
||||
model_processing_panel = fields.Char('模型加工面板', default='')
|
||||
model_remark = fields.Char('模型备注说明')
|
||||
length = fields.Float('长(mm)', digits=(16, 3))
|
||||
width = fields.Float('宽(mm)', digits=(16, 3))
|
||||
@@ -901,15 +903,17 @@ class ResProductMo(models.Model):
|
||||
vals = {
|
||||
'name': product_name,
|
||||
'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_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),
|
||||
'unit_number': item.get('unit_number'),
|
||||
'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))) if not blank_bool else (
|
||||
item.get('blank_length') * item.get('blank_width') * item.get('blank_height')),
|
||||
'product_model_type_id': model_type.id,
|
||||
'model_processing_panel': item['processing_panel_detail'],
|
||||
'model_processing_panel': item['processing_panel_detail'] if item['processing_panel_detail'] else '',
|
||||
'model_machining_precision': item['model_machining_precision'],
|
||||
'model_code': item['barcode'],
|
||||
'length': item['model_long'],
|
||||
@@ -957,7 +961,7 @@ class ResProductMo(models.Model):
|
||||
self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
|
||||
'quality_standard', item['quality_standard_mimetype'])
|
||||
return copy_product_id
|
||||
|
||||
|
||||
def format_float(self, value):
|
||||
# 将浮点数转换为字符串
|
||||
value_str = str(value)
|
||||
|
||||
@@ -21,6 +21,7 @@ from odoo.addons.sf_base.commons.common import Common
|
||||
from odoo.exceptions import UserError
|
||||
from io import BytesIO
|
||||
from odoo.exceptions import ValidationError
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class stockWarehouse(models.Model):
|
||||
@@ -727,6 +728,33 @@ class StockPicking(models.Model):
|
||||
production.workorder_ids.write({'back_button_display': False})
|
||||
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):
|
||||
production = workorders[0].production_id
|
||||
|
||||
@@ -40,7 +40,8 @@ class StockPicking(models.Model):
|
||||
"""
|
||||
|
||||
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 可能存在多个
|
||||
if out_quality_checks:
|
||||
for out_quality_check in out_quality_checks:
|
||||
|
||||
Reference in New Issue
Block a user