diff --git a/sf_demand_plan/__init__.py b/sf_demand_plan/__init__.py new file mode 100644 index 00000000..cec04a5b --- /dev/null +++ b/sf_demand_plan/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from . import controllers +from . import models +from . import wizard diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py new file mode 100644 index 00000000..932685e0 --- /dev/null +++ b/sf_demand_plan/__manifest__.py @@ -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, +} diff --git a/sf_demand_plan/controllers/__init__.py b/sf_demand_plan/controllers/__init__.py new file mode 100644 index 00000000..e046e49f --- /dev/null +++ b/sf_demand_plan/controllers/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/sf_demand_plan/controllers/controllers.py b/sf_demand_plan/controllers/controllers.py new file mode 100644 index 00000000..5db1ebe6 --- /dev/null +++ b/sf_demand_plan/controllers/controllers.py @@ -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) diff --git a/sf_demand_plan/models/__init__.py b/sf_demand_plan/models/__init__.py new file mode 100644 index 00000000..a0554c11 --- /dev/null +++ b/sf_demand_plan/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import sf_production_demand_plan +from . import sale_order diff --git a/sf_demand_plan/models/sale_order.py b/sf_demand_plan/models/sale_order.py new file mode 100644 index 00000000..e1168115 --- /dev/null +++ b/sf_demand_plan/models/sale_order.py @@ -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 diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py new file mode 100644 index 00000000..086412fa --- /dev/null +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -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},请去云平台处理") diff --git a/sf_demand_plan/security/ir.model.access.csv b/sf_demand_plan/security/ir.model.access.csv new file mode 100644 index 00000000..56e8e247 --- /dev/null +++ b/sf_demand_plan/security/ir.model.access.csv @@ -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 diff --git a/sf_demand_plan/static/src/scss/style.css b/sf_demand_plan/static/src/scss/style.css new file mode 100644 index 00000000..f5b687fb --- /dev/null +++ b/sf_demand_plan/static/src/scss/style.css @@ -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; +} diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml new file mode 100644 index 00000000..587bb2ce --- /dev/null +++ b/sf_demand_plan/views/demand_plan.xml @@ -0,0 +1,119 @@ + + + sf.production.demand.plan.tree + sf.production.demand.plan + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +