diff --git a/sf_demand_plan/__init__.py b/sf_demand_plan/__init__.py new file mode 100644 index 00000000..cde864ba --- /dev/null +++ b/sf_demand_plan/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py new file mode 100644 index 00000000..fba85e4c --- /dev/null +++ b/sf_demand_plan/__manifest__.py @@ -0,0 +1,31 @@ +# -*- 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'], + 'data': [ + 'security/ir.model.access.csv', + 'views/demand_plan.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/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..80499fcf --- /dev/null +++ b/sf_demand_plan/models/sale_order.py @@ -0,0 +1,14 @@ +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, + } + self.env['sf.production.demand.plan'].sudo().create(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..8f250aed --- /dev/null +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + + +class sf_production_plan(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([ + ('20', '待确认'), + ('30', '需求确认'), + ('50', '已下达'), + ('100', '取消'), + ], string='状态') + + sale_order_id = fields.Many2one(comodel_name="sale.order", + string="销售订单") + sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", + string="销售订单行") + 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' + + ) + 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) + date_order = fields.Datetime('下单日期', related='sale_order_id.date_order') + plan_remark = fields.Text("计划备注") + + processing_time = fields.Char('程序工时') + planned_start_date = fields.Date('计划开工日期') + actual_start_date = fields.Date('实际开工日期') + actual_end_date = fields.Date('实际完工日期') + print_count = fields.Char('打印次数', default='T0C0') + + sequence = fields.Integer('序号') + + @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.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("计划开工日期必须大于或等于今天。") 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..cb2af3fc --- /dev/null +++ b/sf_demand_plan/security/ir.model.access.csv @@ -0,0 +1,3 @@ +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 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..90b00db8 --- /dev/null +++ b/sf_demand_plan/static/src/scss/style.css @@ -0,0 +1,3 @@ +.demand_plan_tree th:not(.o_list_record_selector,.row_no,[data-name=sequence]) { + min-width: 98px !important; +} \ No newline at end of file diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml new file mode 100644 index 00000000..6cc6a998 --- /dev/null +++ b/sf_demand_plan/views/demand_plan.xml @@ -0,0 +1,95 @@ + + + sf.production.demand.plan.tree + sf.production.demand.plan + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sf.production.demand.plan.search + sf.production.demand.plan + + + + + + + + + + + + + + + + + + + + + + + + + + 需求计划 + ir.actions.act_window + sf.production.demand.plan + tree + + + + + \ No newline at end of file