diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py index 474c1608..27c6f3b3 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', 'data/stock_route_group.xml', diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index 9f53f163..4c7f7ae0 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -8,6 +8,28 @@ from datetime import datetime, timedelta from odoo.exceptions import UserError import re +class ProductCategory(models.Model): + _inherit = 'product.category' + + negative_inventory_allowed = fields.Boolean(string="允许负库存", default=False) + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + whether_show_quality_check = fields.Boolean(string="是否显示质检") + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + blank_type = fields.Selection([ + ('圆料', '圆料'), + ('方料', '方料'), + ], string='坯料分类') + + blank_precision = fields.Selection([ + ('精坯', '精坯'), + ('粗坯', '粗坯'), + ], string='坯料类型') class SfProductionDemandPlan(models.Model): _name = 'sf.production.demand.plan' @@ -687,227 +709,190 @@ class SfProductionDemandPlan(models.Model): }} self.action_confirm() - def action_confirm(self): - self = self.with_context( - demand_plan_line_id=self.id +def action_confirm(self): + self = self.with_context( + demand_plan_line_id=self.id + ) + self.mrp_bom_create() + self._action_launch_stock_rule() + if self.supply_method in ('automation', 'manual'): + self.write({'status': '50'}) + self.update_sale_order_state() + else: + self.write({'status': '60'}) + self.update_sale_order_state() + +def _get_embryo_template_by_supply_method(self): + """ + 根据供货方式获取对应的胚料模板,供mrp_bom_create调用。 + """ + supply_map = { + 'automation': self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo(), + 'outsourcing': self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo(), + 'purchase': self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo(), + 'manual': self.env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo(), + 'material_customer_provided': self.env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo(), + } + template = supply_map.get(self.supply_method) + if not template: + raise UserError(f"未配置供货方式 {self.supply_method} 对应的胚料模板") + return template + +def mrp_bom_create(self): + if self.supply_method in ('automation', 'manual'): + line_ids = self.demand_plan_id.line_ids.filtered( + lambda p: p.supply_method in ('automation', 'manual') and p.status in ('50', '60')) + if line_ids: + self.bom_id = line_ids[0].bom_id.id + return + elif self.supply_method == 'outsourcing': + line_ids = self.demand_plan_id.line_ids.filtered( + lambda p: p.supply_method == 'outsourcing' and p.status == '60') + if line_ids: + self.bom_id = line_ids[0].bom_id.id + return + + bom_type = '' + if self.supply_method == 'automation': + bom_type = 'normal' + product_template_id = self.env.ref('sf_dlm.product_template_sf').sudo().product_tmpl_id + elif self.supply_method == 'outsourcing': + bom_type = 'subcontract' + product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').sudo() + elif self.supply_method == 'purchase': + product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').sudo() + elif self.supply_method == 'manual': + bom_type = 'normal' + product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo() + + self.product_id.product_tmpl_id.copy_template(product_template_id) + future_time = datetime.now() + timedelta(hours=8) + code = f"{self.product_id.default_code}-{bom_type}-{future_time.strftime('%Y%m%d%H%M%S')}" + + order_id = self.sale_order_id + product = self.product_id + item = { + 'texture_code': product.materials_id.materials_no, + 'texture_type_code': product.materials_type_id.materials_no, + 'model_long': product.length, + 'model_width': product.width, + 'model_height': product.height, + 'blank_volume': product.model_volume, + 'blank_area': product.model_area, + 'price': product.list_price, + 'embryo_redundancy_id': self.sale_order_line_id.embryo_redundancy_id, + 'model_id': self.model_id + } + product_name = '' + match = re.search(r'(S\d{5}-\d+)', product.name) + product_seria = 0 + if match: + product_name = match.group(0) + product_seria = int(product_name.split('-')[-1]) + + if self.supply_method != 'purchase': + if self.sale_order_line_id.embryo_redundancy_id: + embryo_template = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo() + embryo_key = 'material_customer_provided' + else: + embryo_template = self._get_embryo_template_by_supply_method() + embryo_key = self.supply_method + + tracking_method = embryo_template.tracking + + embryo_product = self.env['product.template'].sudo().no_bom_product_create( + embryo_template.with_context(active_test=False).product_variant_id, + item, + order_id, + embryo_key, + product_seria, + product ) - self.mrp_bom_create() - self._action_launch_stock_rule() - if self.supply_method in ('automation', 'manual'): - self.write({'status': '50'}) - self.update_sale_order_state() + + if embryo_product == -3: + raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配') + + if embryo_key in ('automation', 'manual', 'material_customer_provided'): + embryo_bom_type = 'normal' + elif embryo_key == 'outsourcing': + embryo_bom_type = 'subcontract' + elif embryo_key == 'purchase': + embryo_bom_type = 'purchase' else: - self.write({'status': '60'}) - self.update_sale_order_state() + embryo_bom_type = 'normal' - def mrp_bom_create(self): - if self.supply_method in ('automation', 'manual'): - line_ids = self.demand_plan_id.line_ids.filtered( - lambda p: p.supply_method in ('automation', 'manual') and p.status in ('50', '60')) - if line_ids: - self.bom_id = line_ids[0].bom_id.id - return - elif self.supply_method == 'outsourcing': - line_ids = self.demand_plan_id.line_ids.filtered( - lambda p: p.supply_method == 'outsourcing' and p.status == '60') - if line_ids: - self.bom_id = line_ids[0].bom_id.id - return - bom_type = '' - # 根据供货方式修改成品模板 - if self.supply_method == 'automation': - bom_type = 'normal' - product_template_id = self.env.ref('sf_dlm.product_template_sf').sudo().product_tmpl_id - elif self.supply_method == 'outsourcing': - bom_type = 'subcontract' - product_template_id = self.env.ref( - 'jikimo_sale_multiple_supply_methods.product_template_outsourcing').sudo() - elif self.supply_method == 'purchase': - product_template_id = self.env.ref( - 'jikimo_sale_multiple_supply_methods.product_template_purchase').sudo() - elif self.supply_method == 'manual': - bom_type = 'normal' - product_template_id = self.env.ref( - 'jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo() + embryo_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create( + embryo_product, embryo_bom_type, True, tracking=tracking_method) - # 复制成品模板上的属性 - self.product_id.product_tmpl_id.copy_template(product_template_id) - future_time = datetime.now() + timedelta(hours=8) - # 生成BOM单据编码 - code = f"{self.product_id.default_code}-{bom_type}-{future_time.strftime('%Y%m%d%H%M%S')}" + embryo_bom_line = embryo_bom.with_user(self.env.ref("base.user_admin")).bom_create_line(embryo_product) + if not embryo_bom_line: + raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配') - order_id = self.sale_order_id - product = self.product_id - # 拼接方法需要的item结构,成品的模型数据信息就是坯料的数据信息 - item = { - 'texture_code': product.materials_id.materials_no, - 'texture_type_code': product.materials_type_id.materials_no, - 'model_long': product.length, - 'model_width': product.width, - 'model_height': product.height, - 'blank_volume': product.model_volume, - 'blank_area': product.model_area, - 'price': product.list_price, - 'embryo_redundancy_id': self.sale_order_line_id.embryo_redundancy_id, - 'model_id': self.model_id - } - product_name = '' - match = re.search(r'(S\d{5}-\d+)', product.name) - product_seria = 0 - # 如果匹配成功,提取结果 - if match: - product_name = match.group(0) - # 获取成品名结尾-n的n - product_seria = int(product_name.split('-')[-1]) + product_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create( + product, bom_type, 'product', code, tracking=tracking_method) + product_bom.with_user(self.env.ref("base.user_admin")).bom_create_line_has(embryo_product) + self.bom_id = product_bom.id - # 成品供货方式为采购则不生成bom - if self.supply_method != 'purchase': - # 当成品上带有客供料选项时,生成坯料时选择“客供料”路线 - if self.sale_order_line_id.embryo_redundancy_id: - # 将成品模板的内容复制到成品上 - customer_provided_embryo = self.env.ref( - 'jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo() - # 创建坯料,客供料的批量不需要创建bom - material_customer_provided_embryo = self.env['product.template'].sudo().no_bom_product_create( - customer_provided_embryo.with_context(active_test=False).product_variant_id, - item, - order_id, 'material_customer_provided', product_seria, product) - # 成品配置bom - product_bom_material_customer_provided = self.env['mrp.bom'].with_user( - self.env.ref("base.user_admin")).bom_create( - product, bom_type, 'product', code) - product_bom_material_customer_provided.with_user( - self.env.ref("base.user_admin")).bom_create_line_has( - material_customer_provided_embryo) - self.bom_id = product_bom_material_customer_provided.id - elif self.product_id.materials_type_id.gain_way == '自加工': - self_machining_id = self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo() - # 创建坯料 - self_machining_embryo = self.env['product.template'].sudo().no_bom_product_create( - self_machining_id, - item, - order_id, 'self_machining', product_seria, product) - # 创建坯料的bom - self_machining_bom = self.env['mrp.bom'].with_user( - self.env.ref("base.user_admin")).bom_create( - self_machining_embryo, 'normal', False) - # 创建坯料里bom的组件 - self_machining_bom_line = self_machining_bom.with_user( - self.env.ref("base.user_admin")).bom_create_line( - self_machining_embryo) - if not self_machining_bom_line: - raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配') - # 产品配置bom - product_bom_self_machining = self.env['mrp.bom'].with_user( - self.env.ref("base.user_admin")).bom_create( - product, bom_type, 'product', code) - product_bom_self_machining.with_user(self.env.ref("base.user_admin")).bom_create_line_has( - self_machining_embryo) - self.bom_id = product_bom_self_machining.id - elif self.product_id.materials_type_id.gain_way == '外协': - outsource_id = self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo() - # 创建坯料 - outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id, - item, - order_id, - 'subcontract', - product_seria, - product) - if outsource_embryo == -3: - raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配') - # 创建坯料的bom - outsource_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create( - outsource_embryo, - 'subcontract', True) - # 创建坯料的bom的组件 - outsource_bom_line = outsource_bom.with_user( - self.env.ref("base.user_admin")).bom_create_line(outsource_embryo) - if not outsource_bom_line: - raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配') - # 产品配置bom - product_bom_outsource = self.env['mrp.bom'].with_user( - self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product', code) - product_bom_outsource.with_user(self.env.ref("base.user_admin")).bom_create_line_has( - outsource_embryo) - self.bom_id = product_bom_outsource.id - elif self.product_id.materials_type_id.gain_way == '采购': - purchase_id = self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo() - purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id, - item, - order_id, - 'purchase', - product_seria, - product) - if purchase_embryo and purchase_embryo == -3: - raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配') - else: - # 产品配置bom - product_bom_purchase = self.env['mrp.bom'].with_user( - self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product', code) - product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has( - purchase_embryo) - self.bom_id = product_bom_purchase.id +def _action_launch_stock_rule(self): + procurements = [] + group_id = self.sale_order_id.procurement_group_id + if not group_id: + group_id = self.env['procurement.group'].create(self._prepare_procurement_group_vals()) + self.sale_order_id.procurement_group_id = group_id + else: + updated_vals = {} + if group_id.partner_id != self.sale_order_id.partner_shipping_id: + updated_vals.update({'partner_id': self.sale_order_id.partner_shipping_id.id}) + if group_id.move_type != self.sale_order_id.picking_policy: + updated_vals.update({'move_type': self.sale_order_id.picking_policy}) + if updated_vals: + group_id.write(updated_vals) + values = self._prepare_procurement_values(group_id=group_id) + line_uom = self.sale_order_line_id.product_uom + quant_uom = self.product_id.uom_id + plan_uom_qty, procurement_uom = line_uom._adjust_uom_quantities(self.plan_uom_qty, quant_uom) + procurements.append(self.env['procurement.group'].Procurement( + self.product_id, plan_uom_qty, procurement_uom, + self.sale_order_id.partner_shipping_id.property_stock_customer, + self.product_id.display_name, self.sale_order_id.name, self.sale_order_id.company_id, values)) + if procurements: + procurement_group = self.env['procurement.group'] + if self.env.context.get('import_file'): + procurement_group = procurement_group.with_context(import_file=False) + procurement_group.run(procurements) + orders = self.mapped('sale_order_id') + for order in orders: + pickings_to_confirm = order.picking_ids.filtered(lambda p: p.state not in ['cancel', 'done']) + if pickings_to_confirm: + pickings_to_confirm.action_confirm() + return True - def _action_launch_stock_rule(self): - procurements = [] - group_id = self.sale_order_id.procurement_group_id - if not group_id: - group_id = self.env['procurement.group'].create(self._prepare_procurement_group_vals()) - self.sale_order_id.procurement_group_id = group_id - else: - updated_vals = {} - if group_id.partner_id != self.sale_order_id.partner_shipping_id: - updated_vals.update({'partner_id': self.sale_order_id.partner_shipping_id.id}) - if group_id.move_type != self.sale_order_id.picking_policy: - updated_vals.update({'move_type': self.sale_order_id.picking_policy}) - if updated_vals: - group_id.write(updated_vals) - values = self._prepare_procurement_values(group_id=group_id) - line_uom = self.sale_order_line_id.product_uom - quant_uom = self.product_id.uom_id - plan_uom_qty, procurement_uom = line_uom._adjust_uom_quantities(self.plan_uom_qty, quant_uom) - procurements.append(self.env['procurement.group'].Procurement( - self.product_id, plan_uom_qty, procurement_uom, - self.sale_order_id.partner_shipping_id.property_stock_customer, - self.product_id.display_name, self.sale_order_id.name, self.sale_order_id.company_id, values)) - if procurements: - procurement_group = self.env['procurement.group'] - if self.env.context.get('import_file'): - procurement_group = procurement_group.with_context(import_file=False) - procurement_group.run(procurements) - orders = self.mapped('sale_order_id') - for order in orders: - pickings_to_confirm = order.picking_ids.filtered(lambda p: p.state not in ['cancel', 'done']) - if pickings_to_confirm: - pickings_to_confirm.action_confirm() - return True +def _prepare_procurement_group_vals(self): + return { + 'name': self.sale_order_id.name, + 'move_type': self.sale_order_id.picking_policy, + 'sale_id': self.sale_order_id.id, + 'partner_id': self.sale_order_id.partner_shipping_id.id, + } - def _prepare_procurement_group_vals(self): - return { - 'name': self.sale_order_id.name, - 'move_type': self.sale_order_id.picking_policy, - 'sale_id': self.sale_order_id.id, - 'partner_id': self.sale_order_id.partner_shipping_id.id, - } - - def _prepare_procurement_values(self, group_id=False): - self.ensure_one() - date_deadline = self.sale_order_id.commitment_date or ( - self.sale_order_id.date_order + timedelta(days=self.sale_order_line_id.customer_lead or 0.0)) - date_planned = date_deadline - timedelta(days=self.sale_order_id.company_id.security_lead) - values = { - 'group_id': group_id, - 'sale_line_id': self.sale_order_line_id.id, - 'date_planned': date_planned, - 'date_deadline': date_deadline, - 'route_ids': self.route_ids, - 'warehouse_id': self.sale_order_id.warehouse_id or False, - 'partner_id': self.sale_order_id.partner_shipping_id.id, - 'product_description_variants': self.sale_order_line_id.with_context( - lang=self.sale_order_id.partner_id.lang)._get_sale_order_line_multiline_description_variants(), - 'company_id': self.sale_order_id.company_id, - 'product_packaging_id': self.sale_order_line_id.product_packaging_id, - 'sequence': self.sale_order_line_id.sequence, - 'demand_plan_line_id': self.id - } - return values +def _prepare_procurement_values(self, group_id=False): + self.ensure_one() + date_deadline = self.sale_order_id.commitment_date or ( + self.sale_order_id.date_order + timedelta(days=self.sale_order_line_id.customer_lead or 0.0)) + date_planned = date_deadline - timedelta(days=self.sale_order_id.company_id.security_lead) + values = { + 'group_id': group_id, + 'sale_line_id': self.sale_order_line_id.id, + 'date_planned': date_planned, + 'date_deadline': date_deadline, + 'route_ids': self.route_ids, + 'warehouse_id': self.sale_order_id.warehouse_id or False, + 'partner_id': self.sale_order_id.partner_shipping_id.id, + 'product_description_variants': self.sale_order_line_id.with_context( + lang=self.sale_order_id.partner_id.lang)._get_sale_order_line_multiline_description_variants(), + 'company_id': self.sale_order_id.company_id, + 'product_packaging_id': self.sale_order_line_id.product_packaging_id, + 'sequence': self.sale_order_line_id.sequence, + 'demand_plan_line_id': self.id + } + return values