diff --git a/jikimo_demand_plan_queue/__init__.py b/jikimo_demand_plan_queue/__init__.py new file mode 100644 index 00000000..a0fdc10f --- /dev/null +++ b/jikimo_demand_plan_queue/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/jikimo_demand_plan_queue/__manifest__.py b/jikimo_demand_plan_queue/__manifest__.py new file mode 100644 index 00000000..83f5e07d --- /dev/null +++ b/jikimo_demand_plan_queue/__manifest__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +{ + 'name': '机企猫 需求计划排程队列', + 'version': '1.0', + 'summary': """ 使用队列进行排程 """, + 'author': 'fox', + 'website': '', + 'category': '', + 'depends': ['queue_job_batch', 'sf_demand_plan'], + 'data': [ + + ], + + 'application': True, + 'installable': True, + 'auto_install': False, + 'license': 'LGPL-3', +} diff --git a/jikimo_demand_plan_queue/models/__init__.py b/jikimo_demand_plan_queue/models/__init__.py new file mode 100644 index 00000000..54314ee3 --- /dev/null +++ b/jikimo_demand_plan_queue/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import production_demand_plan diff --git a/jikimo_demand_plan_queue/models/production_demand_plan.py b/jikimo_demand_plan_queue/models/production_demand_plan.py new file mode 100644 index 00000000..fd51e055 --- /dev/null +++ b/jikimo_demand_plan_queue/models/production_demand_plan.py @@ -0,0 +1,20 @@ +from odoo import models, fields + + +class ProductionDemandPlan(models.Model): + _inherit = 'sf.production.demand.plan' + + + def _do_production_schedule(self, pro_plan_list): + """使用队列进行排程""" + batch_size = 10 + current_time = fields.Datetime.now().strftime('%Y%m%d%H%M%S') + index = 1 + for i in range(0, len(pro_plan_list), batch_size): + batch = self.env['queue.job.batch'].get_new_batch('plan-%s-%s' % (current_time, index)) + pro_plans = pro_plan_list[i:i+batch_size] + pro_plans.with_context( + job_batch=batch + ).with_delay().do_production_schedule() + index += 1 + batch.enqueue() \ No newline at end of file diff --git a/jikimo_frontend/static/src/scss/custom_style.scss b/jikimo_frontend/static/src/scss/custom_style.scss index c668d82e..6ffb824d 100644 --- a/jikimo_frontend/static/src/scss/custom_style.scss +++ b/jikimo_frontend/static/src/scss/custom_style.scss @@ -521,11 +521,6 @@ div:has(.o_required_modifier) > label::before { } } -// 设置表格横向滚动 -.o_list_renderer.o_renderer { - max-width: 100%; - overflow-x: auto; -} // 设置表单页面label文本不换行 .o_form_view .o_group .o_wrap_label .o_form_label { diff --git a/jikimo_purchase_request/models/__init__.py b/jikimo_purchase_request/models/__init__.py index 4d5c92da..f1ba1696 100644 --- a/jikimo_purchase_request/models/__init__.py +++ b/jikimo_purchase_request/models/__init__.py @@ -6,3 +6,4 @@ from . import mrp_production from . import purchase_order from . import stock_rule from . import stock_picking +from . import product_product diff --git a/jikimo_purchase_request/models/mrp_production.py b/jikimo_purchase_request/models/mrp_production.py index 6973d51f..d7cd4b81 100644 --- a/jikimo_purchase_request/models/mrp_production.py +++ b/jikimo_purchase_request/models/mrp_production.py @@ -12,9 +12,7 @@ class MrpProduction(models.Model): if item.product_id.is_customer_provided: item.pr_mp_count = 0 else: - # 由于采购申请合并了所有销售订单行的采购,所以不区分产品 - mrp_names = self.env['mrp.production'].search([('origin', '=', item.origin)]).mapped('name') - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)]) + pr_ids = item._get_purchase_request() item.pr_mp_count = len(pr_ids) # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) @@ -25,8 +23,7 @@ class MrpProduction(models.Model): self.ensure_one() # 由于采购申请合并了所有销售订单行的采购,所以不区分产品 - mrp_names = self.env['mrp.production'].search([('origin', '=', self.origin)]).mapped('name') - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)]) + pr_ids = self._get_purchase_request() action = { 'res_model': 'purchase.request', @@ -44,3 +41,12 @@ class MrpProduction(models.Model): 'view_mode': 'tree,form', }) return action + + def _get_purchase_request(self): + """获取跟制造订单相关的采购申请单(根据采购申请单行项目的产品匹配)""" + mrp_names = self.env['mrp.production'].search([('origin', '=', self.origin)]).mapped('name') + pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)]) + product_list = self.product_id._get_product_include_bom() + pr_line_ids = pr_ids.line_ids.filtered(lambda l: l.product_id in product_list) + return pr_line_ids.mapped('request_id') + \ No newline at end of file diff --git a/jikimo_purchase_request/models/product_product.py b/jikimo_purchase_request/models/product_product.py new file mode 100644 index 00000000..c59fcdb8 --- /dev/null +++ b/jikimo_purchase_request/models/product_product.py @@ -0,0 +1,17 @@ +from odoo import models + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + + def _get_product_include_bom(self): + """获取产品列表(包括所有bom)""" + self.ensure_one() + product_list = [self] + bom_ids = self.bom_ids + while (bom_ids): + bom_product_ids = bom_ids.bom_line_ids.mapped('product_id') + product_list.append(bom_product_ids) + bom_ids = bom_product_ids.bom_ids + return product_list \ No newline at end of file diff --git a/jikimo_purchase_request/models/stock_picking.py b/jikimo_purchase_request/models/stock_picking.py index abac1b1b..97b294f3 100644 --- a/jikimo_purchase_request/models/stock_picking.py +++ b/jikimo_purchase_request/models/stock_picking.py @@ -42,6 +42,6 @@ class StockPicking(models.Model): purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines if purchase_request_lines: purchase_request_lines.move_dest_ids = [ - (4, x.id) for x in backorder_ids.move_ids if x.product_id.id == purchase_request_lines.product_id.id + (4, x.id) for x in backorder_ids.move_ids if x.product_id.id in purchase_request_lines.mapped('product_id.id') ] return res \ No newline at end of file diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py index 932685e0..e65fdca7 100644 --- a/sf_demand_plan/__manifest__.py +++ b/sf_demand_plan/__manifest__.py @@ -10,7 +10,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sf_plan', 'jikimo_printing'], + 'depends': ['sf_plan','jikimo_printing'], 'data': [ 'security/ir.model.access.csv', 'views/demand_plan.xml', @@ -23,6 +23,7 @@ ], 'web.assets_backend': [ 'sf_demand_plan/static/src/scss/style.css', + 'sf_demand_plan/static/src/js/print_demand.js', ] }, 'license': 'LGPL-3', diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index ef11adcc..12214bff 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -39,11 +39,7 @@ class SfProductionDemandPlan(models.Model): 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) + customer_name = fields.Char('客户', related='sale_order_id.customer_name') 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文件地址') @@ -64,7 +60,7 @@ class SfProductionDemandPlan(models.Model): 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) + 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' @@ -73,7 +69,10 @@ class SfProductionDemandPlan(models.Model): "交货数量", 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') + model_long = fields.Char('尺寸(mm)', compute='_compute_model_long') + blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类', + related='product_id.blank_type') + embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_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') @@ -197,6 +196,14 @@ class SfProductionDemandPlan(models.Model): else: line.model_long = 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.materials_id') def _compute_materials_id(self): for line in self: @@ -272,18 +279,21 @@ class SfProductionDemandPlan(models.Model): 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') + @api.depends('sale_order_id.mrp_production_ids.move_raw_ids.reserved_availability') 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, + # 获取完成的制造订单 + done_manufacturing = manufacturing_orders.filtered(lambda mo: mo.state == 'done') + product_qty = sum(done_manufacturing.mapped('product_qty')) + # 需求数量-完成数量 + product_uom_qty = record.product_uom_qty - product_qty + total_reserved_availability = sum(manufacturing_orders.mapped('move_raw_ids.reserved_availability')) + if float_compare(total_reserved_availability, product_uom_qty, precision_rounding=record.product_id.uom_id.rounding) >= 0: record.material_check = '1' # 已齐套 else: @@ -308,13 +318,17 @@ class SfProductionDemandPlan(models.Model): [('name', '=', '1#CNC自动生产线')], limit=1) if sf_production_line: now = datetime.now() - time_part = (now + timedelta(minutes=3)).time() + time_part = (now + timedelta(hours=2)).time() date_part = fields.Date.from_string(self.planned_start_date) date_planned_start = datetime.combine(date_part, time_part) 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() + self._do_production_schedule(pro_plan_list) + + def _do_production_schedule(self, pro_plan_list): + for pro_plan in pro_plan_list: + pro_plan.do_production_schedule() + def button_action_print(self): return { @@ -503,7 +517,10 @@ class SfProductionDemandPlan(models.Model): 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( + 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( diff --git a/sf_demand_plan/static/src/js/print_demand.js b/sf_demand_plan/static/src/js/print_demand.js new file mode 100644 index 00000000..a05638a7 --- /dev/null +++ b/sf_demand_plan/static/src/js/print_demand.js @@ -0,0 +1,256 @@ +odoo.define('sf_demand.print_demand', function (require) { + "use strict"; + + var ListController = require('web.ListController'); + var ListRenderer = require('web.ListRenderer'); + var ListView = require('web.ListView'); + var viewRegistry = require('web.view_registry'); + var { url } = require("@web/core/utils/urls") + + var CustomListRenderer = ListRenderer.extend({ + _render: function () { + var self = this; + this.getParent()?.$buttons.hide(); + + return this._super.apply(this, arguments).then(function () { + if(!self.state.data || !self.state.data.length) return + // 添加图片预览容器到页面左侧 + if (!$('.table-image-preview-container').length) { + self.$el.parent().addClass('custom-table-image-container') + self.$el.before( + `
+ +