Accept Merge Request #2183: (feature/需求计划 -> develop)
Merge Request: 需求计划 Created By: @管欢 Reviewed By: @胡尧 Approved By: @胡尧 Accepted By: @管欢 URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2183
This commit is contained in:
4
sf_demand_plan/__init__.py
Normal file
4
sf_demand_plan/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
32
sf_demand_plan/__manifest__.py
Normal file
32
sf_demand_plan/__manifest__.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 需求计划',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '智能工厂计划管理',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
在本模块,支持齐套检查与下达生产
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['sf_plan', 'jikimo_printing'],
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/demand_plan.xml',
|
||||||
|
'wizard/sf_demand_plan_print_wizard_view.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_qweb': [
|
||||||
|
],
|
||||||
|
'web.assets_backend': [
|
||||||
|
'sf_demand_plan/static/src/scss/style.css',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
sf_demand_plan/controllers/__init__.py
Normal file
1
sf_demand_plan/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import controllers
|
||||||
37
sf_demand_plan/controllers/controllers.py
Normal file
37
sf_demand_plan/controllers/controllers.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from odoo import http, fields, models
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.sf_base.controllers.controllers import MultiInheritController
|
||||||
|
|
||||||
|
|
||||||
|
class SfPlanMrsConnect(http.Controller, MultiInheritController):
|
||||||
|
|
||||||
|
@http.route('/api/demand_plan/update_processing_time', type='json', auth='sf_token', methods=['GET', 'POST'],
|
||||||
|
csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def update_processing_time(self, **kw):
|
||||||
|
"""
|
||||||
|
根据模型id修改程序工时
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
res = {'status': 1, 'message': '成功'}
|
||||||
|
datas = request.httprequest.data
|
||||||
|
ret = json.loads(datas)
|
||||||
|
ret = json.loads(ret['result'])
|
||||||
|
logging.info('根据模型id修改程序工时:%s' % ret)
|
||||||
|
demand_plan = request.env['sf.production.demand.plan'].sudo().search(
|
||||||
|
[('model_id', '=', ret['model_id'])])
|
||||||
|
if demand_plan:
|
||||||
|
demand_plan.write(
|
||||||
|
{'processing_time': ret['total_estimated_time']})
|
||||||
|
else:
|
||||||
|
res = {'status': 0, 'message': '未查到该需求计划'}
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('update_demand_paln error:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '系统解析错误!'
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
4
sf_demand_plan/models/__init__.py
Normal file
4
sf_demand_plan/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import sf_production_demand_plan
|
||||||
|
from . import sale_order
|
||||||
23
sf_demand_plan/models/sale_order.py
Normal file
23
sf_demand_plan/models/sale_order.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class ReSaleOrder(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
def sale_order_create_line(self, product, item):
|
||||||
|
ret = super(ReSaleOrder, self).sale_order_create_line(product, item)
|
||||||
|
vals = {
|
||||||
|
'sale_order_id': ret.order_id.id,
|
||||||
|
'sale_order_line_id': ret.id,
|
||||||
|
}
|
||||||
|
demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
|
||||||
|
if demand_plan.product_id.machining_drawings_name:
|
||||||
|
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
|
||||||
|
wizard_vals = {
|
||||||
|
'demand_plan_id': demand_plan.id,
|
||||||
|
'model_id': demand_plan.model_id,
|
||||||
|
'filename_url': filename_url,
|
||||||
|
'type': '1',
|
||||||
|
}
|
||||||
|
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
||||||
|
return ret
|
||||||
558
sf_demand_plan/models/sf_production_demand_plan.py
Normal file
558
sf_demand_plan/models/sf_production_demand_plan.py
Normal file
@@ -0,0 +1,558 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import ast
|
||||||
|
import json
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tools import float_compare
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class SfProductionDemandPlan(models.Model):
|
||||||
|
_name = 'sf.production.demand.plan'
|
||||||
|
_description = 'sf_production_demand_plan'
|
||||||
|
|
||||||
|
def _get_machining_precision(self):
|
||||||
|
machinings = self.env['sf.machining.accuracy'].sudo().search([])
|
||||||
|
|
||||||
|
list = [(m.sync_id, m.name) for m in machinings]
|
||||||
|
return list
|
||||||
|
|
||||||
|
priority = fields.Selection([
|
||||||
|
('1', '紧急'),
|
||||||
|
('2', '高'),
|
||||||
|
('3', '中'),
|
||||||
|
('4', '低'),
|
||||||
|
], string='优先级', default='3')
|
||||||
|
status = fields.Selection([
|
||||||
|
('10', '草稿'),
|
||||||
|
('20', '待确认'),
|
||||||
|
('30', '需求确认'),
|
||||||
|
('50', '待下达生产'),
|
||||||
|
('60', '已下达'),
|
||||||
|
('100', '取消'),
|
||||||
|
], string='状态', compute='_compute_status', store=True)
|
||||||
|
sale_order_id = fields.Many2one(comodel_name="sale.order",
|
||||||
|
string="销售订单", readonly=True)
|
||||||
|
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line",
|
||||||
|
string="销售订单行", readonly=True)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
related='sale_order_id.company_id',
|
||||||
|
store=True, index=True, precompute=True)
|
||||||
|
partner_id = fields.Many2one(
|
||||||
|
comodel_name='res.partner',
|
||||||
|
related='sale_order_line_id.order_partner_id',
|
||||||
|
string="客户",
|
||||||
|
store=True, index=True)
|
||||||
|
order_remark = fields.Text(related='sale_order_id.remark',
|
||||||
|
string="订单备注", store=True)
|
||||||
|
glb_url = fields.Char(related='sale_order_line_id.glb_url', string='glb文件地址')
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
comodel_name='product.product',
|
||||||
|
related='sale_order_line_id.product_id',
|
||||||
|
string='产品', store=True, index=True)
|
||||||
|
model_id = fields.Char('模型ID', related='product_id.model_id')
|
||||||
|
part_name = fields.Char('零件名称', related='product_id.part_name')
|
||||||
|
part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
|
||||||
|
is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True)
|
||||||
|
supply_method = fields.Selection([
|
||||||
|
('automation', "自动化产线加工"),
|
||||||
|
('manual', "人工线下加工"),
|
||||||
|
('purchase', "外购"),
|
||||||
|
('outsourcing', "委外加工"),
|
||||||
|
], string='供货方式', related='sale_order_line_id.supply_method', store=True)
|
||||||
|
product_uom_qty = fields.Float(
|
||||||
|
string="需求数量",
|
||||||
|
related='sale_order_line_id.product_uom_qty', store=True)
|
||||||
|
deadline_of_delivery = fields.Date('客户交期', related='sale_order_id.deadline_of_delivery', store=True)
|
||||||
|
inventory_quantity_auto_apply = fields.Float(
|
||||||
|
string="成品库存",
|
||||||
|
compute='_compute_inventory_quantity_auto_apply'
|
||||||
|
)
|
||||||
|
qty_delivered = fields.Float(
|
||||||
|
"交货数量", related='sale_order_line_id.qty_delivered')
|
||||||
|
qty_to_deliver = fields.Float(
|
||||||
|
"待交货数量", related='sale_order_line_id.qty_to_deliver')
|
||||||
|
model_long = fields.Char('尺寸', compute='_compute_model_long')
|
||||||
|
materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
|
||||||
|
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度',
|
||||||
|
related='product_id.model_machining_precision')
|
||||||
|
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
|
||||||
|
'plan_process_parameter_rel',
|
||||||
|
string='表面工艺',
|
||||||
|
compute='_compute_model_process_parameters_ids'
|
||||||
|
, store=True
|
||||||
|
)
|
||||||
|
product_remark = fields.Char("产品备注", related='product_id.model_remark')
|
||||||
|
order_code = fields.Char('E-SHOP订单号', related='sale_order_id.order_code')
|
||||||
|
order_state = fields.Selection(
|
||||||
|
string='订单状态',
|
||||||
|
related='sale_order_line_id.state')
|
||||||
|
route_id = fields.Many2one('stock.route', string='路线', related='sale_order_line_id.route_id', store=True)
|
||||||
|
contract_date = fields.Date('合同日期')
|
||||||
|
date_order = fields.Datetime('下单日期', related='sale_order_id.date_order')
|
||||||
|
contract_code = fields.Char('合同号')
|
||||||
|
plan_remark = fields.Text("计划备注")
|
||||||
|
material_check = fields.Selection([
|
||||||
|
('0', "未齐套"),
|
||||||
|
('1', "已齐套"),
|
||||||
|
], string='投料齐套检查', compute='_compute_material_check', store=True)
|
||||||
|
processing_time = fields.Char('程序工时', readonly=True)
|
||||||
|
planned_start_date = fields.Date('计划开工日期')
|
||||||
|
actual_start_date = fields.Date('实际开工日期', compute='_compute_actual_start_date', store=True)
|
||||||
|
actual_end_date = fields.Date('实际完工日期', compute='_compute_actual_end_date', store=True)
|
||||||
|
print_count = fields.Char('打印次数', default='T0C0', readonly=True)
|
||||||
|
sequence = fields.Integer('序号')
|
||||||
|
|
||||||
|
hide_action_open_mrp_production = fields.Boolean(
|
||||||
|
string='显示待工艺确认按钮',
|
||||||
|
compute='_compute_hid_button',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
hide_action_purchase_orders = fields.Boolean(
|
||||||
|
string='显示采购按钮',
|
||||||
|
compute='_compute_hide_action_purchase_orders',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
hide_action_stock_picking = fields.Boolean(
|
||||||
|
string='显示调拨单按钮',
|
||||||
|
compute='_compute_hide_action_stock_picking',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
hide_action_outsourcing_stock_picking = fields.Boolean(
|
||||||
|
string='委外显示调拨单按钮',
|
||||||
|
compute='_compute_hide_action_stock_picking',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
hide_action_view_programming = fields.Boolean(
|
||||||
|
string='显示编程单按钮',
|
||||||
|
compute='_compute_hid_button',
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
outsourcing_purchase_request = fields.Char('委外采购申请单')
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.state', 'sale_order_id.mrp_production_ids.schedule_state', 'sale_order_id.order_line',
|
||||||
|
'sale_order_id.mrp_production_ids.state')
|
||||||
|
def _compute_status(self):
|
||||||
|
for record in self:
|
||||||
|
if record.sale_order_id:
|
||||||
|
sale_order_state = record.sale_order_id.state
|
||||||
|
if sale_order_state in ('draft', 'sent', 'supply method'):
|
||||||
|
record.status = '20' # 待确认
|
||||||
|
if record.supply_method in ('purchase', 'outsourcing') and sale_order_state in (
|
||||||
|
'sale', 'processing', 'physical_distribution', 'delivered',
|
||||||
|
'done') and sale_order_state != 'cancel':
|
||||||
|
record.status = '60' # 已下达
|
||||||
|
if record.supply_method in ('automation', 'manual'):
|
||||||
|
if sale_order_state in (
|
||||||
|
'sale', 'processing', 'physical_distribution', 'delivered',
|
||||||
|
'done') and sale_order_state != 'cancel':
|
||||||
|
record.status = '30' # 需求确认
|
||||||
|
# 检查所有制造订单的排程单状态,有一个为待排程状态,就为待下达生产
|
||||||
|
pending_productions = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.state == 'confirmed' and p.product_id.id == record.product_id.id
|
||||||
|
)
|
||||||
|
if pending_productions:
|
||||||
|
record.status = '50' # 待下达生产
|
||||||
|
# 检查所有制造订单的排程单状态
|
||||||
|
if record.sale_order_id.mrp_production_ids and all(
|
||||||
|
order.product_id == record.product_id and order.schedule_state != '未排' for order in
|
||||||
|
record.sale_order_id.mrp_production_ids):
|
||||||
|
record.status = '60' # 已下达
|
||||||
|
if sale_order_state == 'cancel' or not record.sale_order_line_id:
|
||||||
|
record.status = '100' # 取消
|
||||||
|
|
||||||
|
@api.depends('product_id.part_number', 'product_id.model_name')
|
||||||
|
def _compute_part_number(self):
|
||||||
|
for line in self:
|
||||||
|
if line.product_id:
|
||||||
|
if line.product_id.part_number:
|
||||||
|
line.part_number = line.product_id.part_number
|
||||||
|
else:
|
||||||
|
if line.product_id.model_name:
|
||||||
|
line.part_number = line.product_id.model_name.rsplit('.', 1)[0]
|
||||||
|
else:
|
||||||
|
line.part_number = None
|
||||||
|
|
||||||
|
@api.depends('product_id.length', 'product_id.width', 'product_id.height')
|
||||||
|
def _compute_model_long(self):
|
||||||
|
for line in self:
|
||||||
|
if line.product_id:
|
||||||
|
line.model_long = f"{line.product_id.length}*{line.product_id.width}*{line.product_id.height}"
|
||||||
|
else:
|
||||||
|
line.model_long = None
|
||||||
|
|
||||||
|
@api.depends('product_id.materials_id')
|
||||||
|
def _compute_materials_id(self):
|
||||||
|
for line in self:
|
||||||
|
if line.product_id:
|
||||||
|
line.materials_id = f"{line.product_id.materials_id.name}*{line.product_id.materials_type_id.name}"
|
||||||
|
else:
|
||||||
|
line.materials_id = None
|
||||||
|
|
||||||
|
@api.depends('product_id.model_process_parameters_ids')
|
||||||
|
def _compute_model_process_parameters_ids(self):
|
||||||
|
for line in self:
|
||||||
|
if line.product_id and line.product_id.model_process_parameters_ids:
|
||||||
|
line.model_process_parameters_ids = [(6, 0, line.product_id.model_process_parameters_ids.ids)]
|
||||||
|
else:
|
||||||
|
line.model_process_parameters_ids = [(5, 0, 0)]
|
||||||
|
|
||||||
|
def _compute_inventory_quantity_auto_apply(self):
|
||||||
|
location_id = self.env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id
|
||||||
|
product_ids = self.mapped('product_id').ids
|
||||||
|
if product_ids:
|
||||||
|
quant_data = self.env['stock.quant'].read_group(
|
||||||
|
domain=[
|
||||||
|
('product_id', 'in', product_ids),
|
||||||
|
('location_id', '=', location_id)
|
||||||
|
],
|
||||||
|
fields=['product_id', 'inventory_quantity_auto_apply'],
|
||||||
|
groupby=['product_id']
|
||||||
|
)
|
||||||
|
quantity_map = {item['product_id'][0]: item['inventory_quantity_auto_apply'] for item in quant_data}
|
||||||
|
else:
|
||||||
|
quantity_map = {}
|
||||||
|
for line in self:
|
||||||
|
if line.product_id:
|
||||||
|
line.inventory_quantity_auto_apply = quantity_map.get(line.product_id.id, 0.0)
|
||||||
|
else:
|
||||||
|
line.inventory_quantity_auto_apply = 0.0
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.mrp_production_ids.workorder_ids.date_start')
|
||||||
|
def _compute_actual_start_date(self):
|
||||||
|
for record in self:
|
||||||
|
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
|
||||||
|
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda mo: mo.product_id == record.product_id)
|
||||||
|
if manufacturing_orders:
|
||||||
|
start_dates = [
|
||||||
|
workorder.date_start.date() for mo in manufacturing_orders
|
||||||
|
for workorder in mo.workorder_ids if workorder.date_start
|
||||||
|
]
|
||||||
|
record.actual_start_date = min(start_dates) if start_dates else None
|
||||||
|
else:
|
||||||
|
record.actual_start_date = None
|
||||||
|
else:
|
||||||
|
record.actual_start_date = None
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.mrp_production_ids.workorder_ids.state',
|
||||||
|
'sale_order_id.mrp_production_ids.workorder_ids.date_finished')
|
||||||
|
def _compute_actual_end_date(self):
|
||||||
|
for record in self:
|
||||||
|
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
|
||||||
|
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda mo: mo.product_id == record.product_id)
|
||||||
|
finished_orders = manufacturing_orders.filtered(lambda mo: mo.state == 'done')
|
||||||
|
if len(finished_orders) >= record.product_uom_qty:
|
||||||
|
end_dates = [
|
||||||
|
workorder.date_finished.date() for mo in finished_orders
|
||||||
|
for workorder in mo.workorder_ids if workorder.date_finished
|
||||||
|
]
|
||||||
|
record.actual_end_date = max(end_dates) if end_dates else None
|
||||||
|
else:
|
||||||
|
record.actual_end_date = None
|
||||||
|
else:
|
||||||
|
record.actual_end_date = None
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.mrp_production_ids.move_raw_ids.forecast_availability',
|
||||||
|
'sale_order_id.mrp_production_ids.move_raw_ids.quantity_done')
|
||||||
|
def _compute_material_check(self):
|
||||||
|
for record in self:
|
||||||
|
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
|
||||||
|
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda mo: mo.product_id == record.product_id)
|
||||||
|
if manufacturing_orders and manufacturing_orders.move_raw_ids:
|
||||||
|
total_forecast_availability = sum(manufacturing_orders.mapped('move_raw_ids.forecast_availability'))
|
||||||
|
total_quantity_done = sum(manufacturing_orders.mapped('move_raw_ids.quantity_done'))
|
||||||
|
total_sum = total_forecast_availability + total_quantity_done
|
||||||
|
if float_compare(total_sum, record.product_uom_qty,
|
||||||
|
precision_rounding=record.product_id.uom_id.rounding) >= 0:
|
||||||
|
record.material_check = '1' # 已齐套
|
||||||
|
else:
|
||||||
|
record.material_check = '0' # 未齐套
|
||||||
|
else:
|
||||||
|
record.material_check = None
|
||||||
|
else:
|
||||||
|
record.material_check = None
|
||||||
|
|
||||||
|
@api.constrains('planned_start_date')
|
||||||
|
def _check_planned_start_date(self):
|
||||||
|
for record in self:
|
||||||
|
if record.planned_start_date and record.planned_start_date < fields.Date.today():
|
||||||
|
raise ValidationError("计划开工日期必须大于或等于今天。")
|
||||||
|
|
||||||
|
def release_production_order(self):
|
||||||
|
pro_plan_list = self.env['sf.production.plan'].search(
|
||||||
|
[('product_id', '=', self.product_id.id), ('state', '=', 'draft')])
|
||||||
|
sf_production_line = self.env['sf.production.line'].sudo().search(
|
||||||
|
[('name', '=', '1#CNC自动生产线')], limit=1)
|
||||||
|
current_datetime = datetime.now() + timedelta(minutes=3)
|
||||||
|
current_hour = current_datetime.hour + current_datetime.minute / 60
|
||||||
|
date_planned_start = None
|
||||||
|
production_lines = sf_production_line.mrp_workcenter_ids.filtered(lambda b: "自动生产线" in b.name)
|
||||||
|
if production_lines:
|
||||||
|
if not production_lines.deal_with_workcenter_calendar(current_datetime):
|
||||||
|
attendance_list = production_lines.resource_calendar_id.attendance_ids
|
||||||
|
# 获取所有工作日规则并按星期几分组
|
||||||
|
attendance_by_day = {}
|
||||||
|
for attendance in attendance_list:
|
||||||
|
if attendance.dayofweek not in attendance_by_day:
|
||||||
|
attendance_by_day[attendance.dayofweek] = []
|
||||||
|
attendance_by_day[attendance.dayofweek].append(attendance)
|
||||||
|
|
||||||
|
for day_offset in range(0, 8):
|
||||||
|
check_date = current_datetime + timedelta(days=day_offset)
|
||||||
|
# 日期为星期几
|
||||||
|
check_day = production_lines.get_current_day_of_week(check_date)
|
||||||
|
if check_day in attendance_by_day:
|
||||||
|
day_attendances = attendance_by_day[check_day]
|
||||||
|
if day_offset == 0:
|
||||||
|
for attendance in day_attendances:
|
||||||
|
if current_hour < attendance.hour_to:
|
||||||
|
# 找到下一个有效时间段
|
||||||
|
if current_hour < attendance.hour_from:
|
||||||
|
# 使用开始时间
|
||||||
|
date_planned_start = check_date.replace(
|
||||||
|
hour=int(attendance.hour_from),
|
||||||
|
minute=int((attendance.hour_from % 1) * 60),
|
||||||
|
second=0,
|
||||||
|
microsecond=0
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 不是今天,使用第一个工作时间段
|
||||||
|
attendance = day_attendances[0]
|
||||||
|
date_planned_start = check_date.replace(
|
||||||
|
hour=int(attendance.hour_from),
|
||||||
|
minute=int((attendance.hour_from % 1) * 60),
|
||||||
|
second=0,
|
||||||
|
microsecond=0
|
||||||
|
)
|
||||||
|
|
||||||
|
if date_planned_start:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
date_planned_start = current_datetime
|
||||||
|
|
||||||
|
if date_planned_start:
|
||||||
|
pro_plan_list.production_line_id = sf_production_line.id
|
||||||
|
pro_plan_list.date_planned_start = date_planned_start
|
||||||
|
for pro_plan in pro_plan_list:
|
||||||
|
pro_plan.do_production_schedule()
|
||||||
|
|
||||||
|
def button_action_print(self):
|
||||||
|
return {
|
||||||
|
'res_model': 'sf.demand.plan.print.wizard',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': _("打印"),
|
||||||
|
'domain': [('demand_plan_id', 'in', self.ids)],
|
||||||
|
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.mrp_production_ids.state', 'sale_order_id.mrp_production_ids.programming_state')
|
||||||
|
def _compute_hid_button(self):
|
||||||
|
for record in self:
|
||||||
|
mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == record.product_id.id
|
||||||
|
)
|
||||||
|
record.hide_action_open_mrp_production = bool(mrp_production_ids) and record.supply_method in (
|
||||||
|
'automation', 'manual')
|
||||||
|
programming_mrp_production_ids = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.programming_state == '编程中' and p.product_id.id == record.product_id.id
|
||||||
|
)
|
||||||
|
record.hide_action_view_programming = bool(programming_mrp_production_ids)
|
||||||
|
|
||||||
|
def _compute_hide_action_purchase_orders(self):
|
||||||
|
for record in self:
|
||||||
|
record.hide_action_purchase_orders = False
|
||||||
|
outsourcing_purchase_request = []
|
||||||
|
if record.supply_method in ('automation',
|
||||||
|
'manual') and record.material_check == '0' and not record.sale_order_line_id.is_incoming_material:
|
||||||
|
mrp_production = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.product_id.id == record.product_id.id
|
||||||
|
).sorted(key=lambda p: p.id)
|
||||||
|
if mrp_production:
|
||||||
|
raw_materials = mrp_production.mapped('move_raw_ids.product_id')
|
||||||
|
if raw_materials:
|
||||||
|
purchase_orders = self.env['purchase.order'].sudo().search([
|
||||||
|
('state', '=', 'purchase'),
|
||||||
|
('order_line.product_id', 'in', raw_materials.ids)
|
||||||
|
])
|
||||||
|
total_purchase_quantity = sum(
|
||||||
|
sum(
|
||||||
|
order.order_line.filtered(
|
||||||
|
lambda line: line.product_id in raw_materials
|
||||||
|
).mapped('product_qty')
|
||||||
|
)
|
||||||
|
for order in purchase_orders
|
||||||
|
)
|
||||||
|
if total_purchase_quantity < record.product_uom_qty:
|
||||||
|
pr_ids = self.env['purchase.request'].sudo().search(
|
||||||
|
[('origin', 'like', mrp_production[0].name), ('state', '!=', 'done')])
|
||||||
|
outsourcing_purchase_request.extend(pr_ids.ids)
|
||||||
|
elif record.supply_method in ('purchase', 'outsourcing'):
|
||||||
|
pr_ids = None
|
||||||
|
purchase_orders = self.env['purchase.order'].sudo().search([
|
||||||
|
('state', 'in', ('purchase', 'done')),
|
||||||
|
('order_line.product_id', '=', record.product_id.id)
|
||||||
|
])
|
||||||
|
total_purchase_quantity = sum(
|
||||||
|
sum(
|
||||||
|
order.order_line.filtered(
|
||||||
|
lambda line: line.product_id in record.product_id
|
||||||
|
).mapped('product_qty')
|
||||||
|
)
|
||||||
|
for order in purchase_orders
|
||||||
|
)
|
||||||
|
if total_purchase_quantity < record.product_uom_qty:
|
||||||
|
pr_ids = self.env['purchase.request'].sudo().search(
|
||||||
|
[('origin', 'like', record.sale_order_id.name), ('state', '!=', 'done')])
|
||||||
|
outsourcing_purchase_request.extend(pr_ids.ids)
|
||||||
|
if record.supply_method == 'outsourcing' and not record.sale_order_line_id.is_incoming_material:
|
||||||
|
bom_line_ids = record.product_id.bom_ids.bom_line_ids
|
||||||
|
# BOM_数量
|
||||||
|
total_product_qty = sum(line.product_qty for line in bom_line_ids)
|
||||||
|
bom_product_ids = bom_line_ids.mapped('product_id')
|
||||||
|
product_purchase_orders = self.env['purchase.order'].sudo().search([
|
||||||
|
('state', 'in', ('purchase', 'done')),
|
||||||
|
('order_line.product_id', 'in', bom_product_ids.ids)
|
||||||
|
])
|
||||||
|
# 购订单_数量
|
||||||
|
total_outsourcing_purchase_quantity = sum(
|
||||||
|
sum(
|
||||||
|
order.order_line.filtered(
|
||||||
|
lambda line: line.product_id in bom_product_ids
|
||||||
|
).mapped('product_qty')
|
||||||
|
)
|
||||||
|
for order in product_purchase_orders
|
||||||
|
)
|
||||||
|
if total_outsourcing_purchase_quantity / total_product_qty < record.product_uom_qty:
|
||||||
|
purchase_request = self.env['purchase.request'].sudo().search(
|
||||||
|
[('line_ids.product_id', 'in', bom_product_ids.ids),
|
||||||
|
('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')])
|
||||||
|
outsourcing_purchase_request.extend(purchase_request.ids)
|
||||||
|
record.outsourcing_purchase_request = json.dumps(outsourcing_purchase_request)
|
||||||
|
if outsourcing_purchase_request:
|
||||||
|
record.hide_action_purchase_orders = True
|
||||||
|
|
||||||
|
@api.depends('sale_order_id.mrp_production_ids.picking_ids.state', 'sale_order_id.picking_ids.state')
|
||||||
|
def _compute_hide_action_stock_picking(self):
|
||||||
|
for record in self:
|
||||||
|
record.hide_action_stock_picking = False
|
||||||
|
record.hide_action_outsourcing_stock_picking = False
|
||||||
|
if record.supply_method in ('automation', 'manual'):
|
||||||
|
manufacturing_orders = record.sale_order_id.mrp_production_ids
|
||||||
|
record.hide_action_stock_picking = bool(manufacturing_orders.mapped('picking_ids').filtered(
|
||||||
|
lambda p: p.state == 'assigned'))
|
||||||
|
elif record.supply_method in ('purchase', 'outsourcing'):
|
||||||
|
assigned_picking_ids = record.sale_order_id.picking_ids.filtered(
|
||||||
|
lambda
|
||||||
|
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in record.product_id)
|
||||||
|
if record.supply_method == 'outsourcing':
|
||||||
|
outsourcing_assigned_picking_ids = record.get_outsourcing_picking_ids()
|
||||||
|
record.hide_action_outsourcing_stock_picking = outsourcing_assigned_picking_ids
|
||||||
|
record.hide_action_stock_picking = assigned_picking_ids or outsourcing_assigned_picking_ids
|
||||||
|
else:
|
||||||
|
record.hide_action_stock_picking = assigned_picking_ids
|
||||||
|
|
||||||
|
def get_outsourcing_picking_ids(self):
|
||||||
|
order_ids = self.env['purchase.order'].sudo().search(
|
||||||
|
[('order_line.product_id', 'in', self.product_id.ids),
|
||||||
|
('purchase_type', '=', 'outsourcing')])
|
||||||
|
outsourcing_picking_ids = order_ids._get_subcontracting_resupplies()
|
||||||
|
outsourcing_assigned_picking_ids = outsourcing_picking_ids.filtered(lambda p: p.state == 'assigned')
|
||||||
|
return outsourcing_assigned_picking_ids
|
||||||
|
|
||||||
|
def action_open_sale_order(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'sale.order',
|
||||||
|
'res_id': self.sale_order_id.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_open_mrp_production(self):
|
||||||
|
self.ensure_one()
|
||||||
|
mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.state == 'technology_to_confirmed' and p.product_id.id == self.product_id.id
|
||||||
|
)
|
||||||
|
action = {
|
||||||
|
'res_model': 'mrp.production',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
if len(mrp_production_ids) == 1:
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': mrp_production_ids.id,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action.update({
|
||||||
|
'name': _("制造订单列表"),
|
||||||
|
'domain': [('id', 'in', mrp_production_ids.ids)],
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
def action_view_purchase_request(self):
|
||||||
|
self.ensure_one()
|
||||||
|
pr_ids = self.env['purchase.request'].sudo().search(
|
||||||
|
[('id', 'in', ast.literal_eval(self.outsourcing_purchase_request))])
|
||||||
|
action = {
|
||||||
|
'res_model': 'purchase.request',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
if len(pr_ids) == 1:
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': pr_ids[0].id,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action.update({
|
||||||
|
'name': _("采购申请"),
|
||||||
|
'domain': [('id', 'in', pr_ids.ids)],
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
def action_view_stock_picking(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
|
||||||
|
picking_ids = None
|
||||||
|
if self.supply_method in ('automation', 'manual'):
|
||||||
|
picking_ids = self.sale_order_id.mrp_production_ids.mapped('picking_ids').filtered(
|
||||||
|
lambda p: p.state == 'assigned')
|
||||||
|
elif self.supply_method in ('purchase', 'outsourcing'):
|
||||||
|
picking_ids = self.sale_order_id.picking_ids.filtered(
|
||||||
|
lambda
|
||||||
|
p: p.state == 'assigned' and p.picking_type_id.name != '发料出库' and p.move_line_ids.product_id in self.product_id)
|
||||||
|
if self.supply_method == 'outsourcing' and self.hide_action_outsourcing_stock_picking:
|
||||||
|
picking_ids = picking_ids.union(self.get_outsourcing_picking_ids())
|
||||||
|
if picking_ids:
|
||||||
|
if len(picking_ids) > 1:
|
||||||
|
action['domain'] = [('id', 'in', picking_ids.ids)]
|
||||||
|
elif picking_ids:
|
||||||
|
action['res_id'] = picking_ids.id
|
||||||
|
action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
|
||||||
|
if 'views' in action:
|
||||||
|
action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
|
||||||
|
return action
|
||||||
|
|
||||||
|
def action_view_programming(self):
|
||||||
|
self.ensure_one()
|
||||||
|
programming_mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.programming_state == '编程中' and p.product_id.id == self.product_id.id
|
||||||
|
).mapped('programming_no')
|
||||||
|
if programming_mrp_production_ids:
|
||||||
|
programming_no = list(set(programming_mrp_production_ids))
|
||||||
|
numbers_str = "、".join(programming_no)
|
||||||
|
raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
|
||||||
6
sf_demand_plan/security/ir.model.access.csv
Normal file
6
sf_demand_plan/security/ir.model.access.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_sf_production_demand_plan,sf.production.demand.plan,model_sf_production_demand_plan,base.group_user,1,0,0,0
|
||||||
|
access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for dispatch,model_sf_production_demand_plan,sf_base.group_plan_dispatch,1,1,0,0
|
||||||
|
|
||||||
|
access_sf_demand_plan_print_wizard,sf.demand.plan.print.wizard,model_sf_demand_plan_print_wizard,base.group_user,1,0,0,0
|
||||||
|
access_sf_demand_plan_print_wizard_for_dispatch,sf.demand.plan.print.wizard for dispatch,model_sf_demand_plan_print_wizard,sf_base.group_plan_dispatch,1,1,0,0
|
||||||
|
11
sf_demand_plan/static/src/scss/style.css
Normal file
11
sf_demand_plan/static/src/scss/style.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.demand_plan_tree .o_list_table_ungrouped th:not(.o_list_record_selector,.row_no,[data-name=sequence]) {
|
||||||
|
min-width: 98px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demand_plan_tree .o_list_table_grouped th:not(.o_list_record_selector,.row_no,[data-name=sequence]) {
|
||||||
|
width: 98px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demand_plan_tree .o_list_table_ungrouped {
|
||||||
|
min-width: 1900px;
|
||||||
|
}
|
||||||
119
sf_demand_plan/views/demand_plan.xml
Normal file
119
sf_demand_plan/views/demand_plan.xml
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<odoo>
|
||||||
|
<record id="view_sf_production_demand_plan_tree" model="ir.ui.view">
|
||||||
|
<field name="name">sf.production.demand.plan.tree</field>
|
||||||
|
<field name="model">sf.production.demand.plan</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="需求计划" default_order="create_date desc" editable="bottom" class="demand_plan_tree">
|
||||||
|
<header>
|
||||||
|
<button string="打印" name="button_action_print" type="object"
|
||||||
|
class="btn-primary"/>
|
||||||
|
</header>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="id" optional="hide"/>
|
||||||
|
<field name="priority"/>
|
||||||
|
<field name="status"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="order_remark"/>
|
||||||
|
<field name="glb_url" optional="hide"/>
|
||||||
|
<field name="product_id"/>
|
||||||
|
<field name="model_id" optional="hide"/>
|
||||||
|
<field name="part_name"/>
|
||||||
|
<field name="part_number"/>
|
||||||
|
<field name="is_incoming_material"/>
|
||||||
|
<field name="supply_method"/>
|
||||||
|
<field name="product_uom_qty"/>
|
||||||
|
<field name="deadline_of_delivery"/>
|
||||||
|
<field name="inventory_quantity_auto_apply"/>
|
||||||
|
<field name="qty_delivered"/>
|
||||||
|
<field name="qty_to_deliver"/>
|
||||||
|
<field name="model_long"/>
|
||||||
|
<field name="materials_id"/>
|
||||||
|
<field name="model_machining_precision"/>
|
||||||
|
<field name="model_process_parameters_ids" widget="many2many_tags"/>
|
||||||
|
<field name="product_remark" optional="hide"/>
|
||||||
|
<field name="order_code" optional="hide"/>
|
||||||
|
<field name="sale_order_id" optional="hide"/>
|
||||||
|
<field name="sale_order_line_id" optional="hide"/>
|
||||||
|
<field name="order_state"/>
|
||||||
|
<field name="route_id" optional="hide"/>
|
||||||
|
<field name="contract_date"/>
|
||||||
|
<field name="date_order"/>
|
||||||
|
<field name="contract_code"/>
|
||||||
|
<field name="plan_remark"/>
|
||||||
|
<field name="processing_time"/>
|
||||||
|
<field name="material_check"/>
|
||||||
|
<field name="hide_action_open_mrp_production" invisible="1"/>
|
||||||
|
<field name="hide_action_purchase_orders" invisible="1"/>
|
||||||
|
<field name="hide_action_stock_picking" invisible="1"/>
|
||||||
|
<field name="hide_action_view_programming" invisible="1"/>
|
||||||
|
<button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('supply_method', '!=', False)]}"/>
|
||||||
|
<button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>
|
||||||
|
<button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>
|
||||||
|
<button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>
|
||||||
|
<button name="action_view_programming" type="object" string="编程单" class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>
|
||||||
|
<field name="planned_start_date"/>
|
||||||
|
<field name="actual_start_date"/>
|
||||||
|
<field name="actual_end_date"/>
|
||||||
|
<field name="create_date" optional="hide" string="创建时间"/>
|
||||||
|
<field name="create_uid" optional="hide" string="创建人"/>
|
||||||
|
<field name="write_date" string="更新时间"/>
|
||||||
|
<field name="write_uid" optional="hide" string="更新人"/>
|
||||||
|
<field name="print_count"/>
|
||||||
|
<button name="release_production_order" type="object" string="下达生产" class="btn-primary"
|
||||||
|
attrs="{'invisible': ['|',('status', '!=', '50'), ('supply_method', 'not in', ['automation', 'manual'])]}"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_sf_production_demand_plan_search" model="ir.ui.view">
|
||||||
|
<field name="name">sf.production.demand.plan.search</field>
|
||||||
|
<field name="model">sf.production.demand.plan</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="order_remark"/>
|
||||||
|
<field name="product_id"/>
|
||||||
|
<field name="part_name"/>
|
||||||
|
<field name="part_number"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="supply_method"/>
|
||||||
|
<field name="materials_id"/>
|
||||||
|
<field name="model_process_parameters_ids"/>
|
||||||
|
<field name="plan_remark"/>
|
||||||
|
<group expand="0" string="Group By">
|
||||||
|
<filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/>
|
||||||
|
<filter name="group_by_status" string="状态" domain="[]" context="{'group_by': 'status'}"/>
|
||||||
|
<filter name="group_by_partner_id" string="客户" domain="[]" context="{'group_by': 'partner_id'}"/>
|
||||||
|
<filter name="group_by_is_incoming_material" string="客供料" domain="[]"
|
||||||
|
context="{'group_by': 'is_incoming_material'}"/>
|
||||||
|
<filter name="group_by_supply_method" string="供货方式" domain="[]"
|
||||||
|
context="{'group_by': 'supply_method'}"/>
|
||||||
|
<filter name="group_by_deadline_of_delivery" string="客户交期" domain="[]"
|
||||||
|
context="{'group_by': 'deadline_of_delivery'}"/>
|
||||||
|
<filter name="group_by_materials_id" string="材料" domain="[]"
|
||||||
|
context="{'group_by': 'materials_id'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sf_production_demand_plan_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">需求计划</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.production.demand.plan</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="demand_plan_menu"
|
||||||
|
name="需求计划"
|
||||||
|
sequence="140"
|
||||||
|
action="sf_production_demand_plan_action"
|
||||||
|
parent="sf_plan.sf_production_plan_menu"
|
||||||
|
/>
|
||||||
|
</odoo>
|
||||||
1
sf_demand_plan/wizard/__init__.py
Normal file
1
sf_demand_plan/wizard/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import sf_demand_plan_print_wizard
|
||||||
79
sf_demand_plan/wizard/sf_demand_plan_print_wizard.py
Normal file
79
sf_demand_plan/wizard/sf_demand_plan_print_wizard.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SfDemandPlanPrintWizard(models.TransientModel):
|
||||||
|
_name = 'sf.demand.plan.print.wizard'
|
||||||
|
_description = u'打印向导'
|
||||||
|
|
||||||
|
demand_plan_id = fields.Many2one('sf.production.demand.plan', string='需求计划ID')
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
comodel_name='product.product',
|
||||||
|
related='demand_plan_id.product_id',
|
||||||
|
string='产品', store=True, index=True)
|
||||||
|
model_id = fields.Char('模型ID')
|
||||||
|
filename_url = fields.Char('文件名/URL')
|
||||||
|
type = fields.Selection([
|
||||||
|
('1', '图纸'),
|
||||||
|
('2', '程序单'),
|
||||||
|
], string='类型')
|
||||||
|
status = fields.Selection([
|
||||||
|
('not_start', '未开始'),
|
||||||
|
('success', '成功'),
|
||||||
|
('fail', '失败'),
|
||||||
|
], string='状态', default='not_start')
|
||||||
|
machining_drawings = fields.Binary('2D加工图纸', related='product_id.machining_drawings', store=True)
|
||||||
|
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
||||||
|
cnc_worksheet = fields.Binary('程序单')
|
||||||
|
|
||||||
|
def demand_plan_print(self):
|
||||||
|
for record in self:
|
||||||
|
pdf_data = record.machining_drawings if record.type == '1' else record.cnc_worksheet
|
||||||
|
if pdf_data:
|
||||||
|
try:
|
||||||
|
# 执行打印
|
||||||
|
self.env['jikimo.printing'].sudo().print_pdf(pdf_data)
|
||||||
|
record.status = 'success'
|
||||||
|
t_part, c_part = record.demand_plan_id.print_count.split('C')
|
||||||
|
t_num = int(t_part[1:])
|
||||||
|
c_num = int(c_part)
|
||||||
|
if record.type == '1':
|
||||||
|
t_num += 1
|
||||||
|
elif record.type == '2':
|
||||||
|
c_num += 1
|
||||||
|
record.demand_plan_id.print_count = f"T{t_num}C{c_num}"
|
||||||
|
except Exception as e:
|
||||||
|
record.status = 'fail'
|
||||||
|
_logger.error(f"文件{record.filename_url}打印失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
class MrpWorkorder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
res = super(MrpWorkorder, self).write(vals)
|
||||||
|
for record in self:
|
||||||
|
if 'cnc_worksheet' in vals:
|
||||||
|
demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search(
|
||||||
|
[('workorder_id', '=', record.id)])
|
||||||
|
if demand_plan_print:
|
||||||
|
self.env['sf.demand.plan.print.wizard'].sudo().write(
|
||||||
|
{'cnc_worksheet': res.cnc_worksheet, 'filename_url': record.cnc_worksheet_name})
|
||||||
|
else:
|
||||||
|
demand_plan = self.env['sf.production.demand.plan'].sudo().search(
|
||||||
|
[('product_id', '=', record.product_id.id)])
|
||||||
|
if demand_plan:
|
||||||
|
wizard_vals = {
|
||||||
|
'demand_plan_id': demand_plan.id,
|
||||||
|
'model_id': demand_plan.model_id,
|
||||||
|
'type': '2',
|
||||||
|
'workorder_id': record.id,
|
||||||
|
'cnc_worksheet': record.cnc_worksheet,
|
||||||
|
'filename_url': record.cnc_worksheet_name
|
||||||
|
}
|
||||||
|
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
||||||
|
return res
|
||||||
17
sf_demand_plan/wizard/sf_demand_plan_print_wizard_view.xml
Normal file
17
sf_demand_plan/wizard/sf_demand_plan_print_wizard_view.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="action_plan_print_tree" model="ir.ui.view">
|
||||||
|
<field name="name">sf.demand.plan.print.wizard.tree</field>
|
||||||
|
<field name="model">sf.demand.plan.print.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="打印">
|
||||||
|
<field name="model_id"/>
|
||||||
|
<field name="filename_url"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||||
|
<field name="cnc_worksheet" widget="pdf_viewer"/>
|
||||||
|
<field name="status"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@@ -334,6 +334,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
|
|
||||||
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
|
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
|
||||||
technology_design_id = fields.Many2one('sf.technology.design')
|
technology_design_id = fields.Many2one('sf.technology.design')
|
||||||
|
cnc_worksheet_name = fields.Char('工作指令文件名', readonly=True)
|
||||||
|
|
||||||
def _compute_default_construction_period_status(self):
|
def _compute_default_construction_period_status(self):
|
||||||
need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected', 'done']
|
need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected', 'done']
|
||||||
|
|||||||
@@ -91,12 +91,15 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
||||||
files_panel = os.listdir(program_path_tmp_panel)
|
files_panel = os.listdir(program_path_tmp_panel)
|
||||||
panel_file_path = ''
|
panel_file_path = ''
|
||||||
|
panel_file_name = ''
|
||||||
if files_panel:
|
if files_panel:
|
||||||
for file in files_panel:
|
for file in files_panel:
|
||||||
file_extension = os.path.splitext(file)[1]
|
file_extension = os.path.splitext(file)[1]
|
||||||
if file_extension.lower() == '.pdf':
|
if file_extension.lower() == '.pdf':
|
||||||
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||||
|
panel_file_name = os.path.splitext(file)[0]
|
||||||
logging.info('panel_file_path:%s' % panel_file_path)
|
logging.info('panel_file_path:%s' % panel_file_path)
|
||||||
|
logging.info('panel_file_name:%s' % panel_file_name)
|
||||||
|
|
||||||
# 向编程单中添加二维码
|
# 向编程单中添加二维码
|
||||||
request.env['printing.utils'].add_qr_code_to_pdf(
|
request.env['printing.utils'].add_qr_code_to_pdf(
|
||||||
@@ -105,7 +108,8 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
"模型ID:%s" % model_id,
|
"模型ID:%s" % model_id,
|
||||||
"零件图号:%s" % part_number if part_number else None
|
"零件图号:%s" % part_number if part_number else None
|
||||||
)
|
)
|
||||||
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read()),
|
||||||
|
'cnc_worksheet_name': panel_file_name})
|
||||||
pre_workorder = productions.workorder_ids.filtered(
|
pre_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
|
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
|
||||||
'cancel'] and ap.processing_panel == panel)
|
'cancel'] and ap.processing_panel == panel)
|
||||||
|
|||||||
Reference in New Issue
Block a user