diff --git a/jikimo_purchase_request_tier_validation/models/stock_rule.py b/jikimo_purchase_request_tier_validation/models/stock_rule.py
index 6bc521f2..366fb633 100644
--- a/jikimo_purchase_request_tier_validation/models/stock_rule.py
+++ b/jikimo_purchase_request_tier_validation/models/stock_rule.py
@@ -4,9 +4,10 @@ class StockRule(models.Model):
_inherit = 'stock.rule'
def _run_buy(self, procurements):
- res = super(StockRule, self)._run_buy(procurements)
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
origins = list(set([procurement[0].origin for procurement in procurements]))
+ res = super(StockRule, self)._run_buy(procurements)
+ # origins = list(set([procurement[0].origin for procurement in procurements]))
for origin in origins:
pr_ids = self.env["purchase.request"].sudo().search(
[('origin', 'like', origin), ('rule_new_add', '=', True), ('state', '=', 'draft')])
diff --git a/sf_demand_plan/models/purchase_order.py b/sf_demand_plan/models/purchase_order.py
index 5bc07313..b9802866 100644
--- a/sf_demand_plan/models/purchase_order.py
+++ b/sf_demand_plan/models/purchase_order.py
@@ -5,38 +5,42 @@ from odoo.tools import float_compare
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
- demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
- string="需求计划明细", readonly=True)
-
def button_confirm(self):
- if self.demand_plan_line_id:
+ if self.order_line[0].demand_plan_line_id:
self = self.with_context(
- demand_plan_line_id=self.demand_plan_line_id.id
+ demand_plan_line_id=self.order_line[0].demand_plan_line_id.id
)
res = super(PurchaseOrder, self).button_confirm()
return res
- @api.depends('origin', 'demand_plan_line_id')
+ @api.depends('origin')
def _compute_purchase_type(self):
for purchase in self:
if purchase.order_line[0].product_id.categ_id.name == '坯料':
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
purchase.purchase_type = 'outsourcing'
else:
- if purchase.demand_plan_line_id.supply_method == 'outsourcing':
+ if purchase.order_line[0].demand_plan_line_id.supply_method == 'outsourcing':
purchase.purchase_type = 'outsourcing'
- elif purchase.demand_plan_line_id.supply_method == 'purchase':
+ elif purchase.order_line[0].demand_plan_line_id.supply_method == 'purchase':
purchase.purchase_type = 'outside'
+
+class PurchaseOrderLine(models.Model):
+ _inherit = 'purchase.order.line'
+
+ demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
+ string="需求计划明细", readonly=True)
+
@api.model
def create(self, vals):
- res = super(PurchaseOrder, self).create(vals)
+ res = super(PurchaseOrderLine, self).create(vals)
if not res.demand_plan_line_id:
- origin = [origin.replace(' ', '') for origin in res.origin.split(',')]
+ origin = [origin.replace(' ', '') for origin in res.order_id.origin.split(',')]
if self.env.context.get('demand_plan_line_id'):
res.demand_plan_line_id = self.env.context.get('demand_plan_line_id')
- elif 'MO' in res.origin:
+ elif 'MO' in res.order_id.origin:
# 原单据是制造订单
mp_ids = self.env['mrp.production'].sudo().search([('name', 'in', origin)])
if mp_ids:
diff --git a/sf_demand_plan/models/purchase_request.py b/sf_demand_plan/models/purchase_request.py
index 9cb15518..d1539976 100644
--- a/sf_demand_plan/models/purchase_request.py
+++ b/sf_demand_plan/models/purchase_request.py
@@ -28,83 +28,8 @@ class PurchaseRequestLine(models.Model):
class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
_inherit = "purchase.request.line.make.purchase.order"
- def make_purchase_order(self):
- res = []
- purchase_obj = self.env["purchase.order"]
- po_line_obj = self.env["purchase.order.line"]
- purchase = False
-
- if len(set([item_id.line_id.supply_method for item_id in self.item_ids])) > 1:
- raise ValidationError('不同供货方式不可合并创建询价单!')
-
- for item in self.item_ids:
- line = item.line_id
- if item.product_qty <= 0.0:
- raise UserError(_("Enter a positive quantity."))
- if self.purchase_order_id:
- purchase = self.purchase_order_id
- if not purchase:
- po_data = self._prepare_purchase_order(
- line.request_id.picking_type_id,
- line.request_id.group_id,
- line.company_id,
- line.request_id.origin,
- )
- po_data['demand_plan_line_id'] = item.line_id.demand_plan_line_id.id
- # po_data.update({'related_product':line.related_product.id})
- purchase = purchase_obj.create(po_data)
-
- # Look for any other PO line in the selected PO with same
- # product and UoM to sum quantities instead of creating a new
- # po line
- domain = self._get_order_line_search_domain(purchase, item)
- available_po_lines = po_line_obj.search(domain)
- new_pr_line = True
- # If Unit of Measure is not set, update from wizard.
- if not line.product_uom_id:
- line.product_uom_id = item.product_uom_id
- # Allocation UoM has to be the same as PR line UoM
- alloc_uom = line.product_uom_id
- wizard_uom = item.product_uom_id
- if available_po_lines and not item.keep_description:
- new_pr_line = False
- po_line = available_po_lines[0]
- po_line.purchase_request_lines = [(4, line.id)]
- po_line.move_dest_ids |= line.move_dest_ids
- po_line_product_uom_qty = po_line.product_uom._compute_quantity(
- po_line.product_uom_qty, alloc_uom
- )
- wizard_product_uom_qty = wizard_uom._compute_quantity(
- item.product_qty, alloc_uom
- )
- all_qty = min(po_line_product_uom_qty, wizard_product_uom_qty)
- self.create_allocation(po_line, line, all_qty, alloc_uom)
- else:
- po_line_data = self._prepare_purchase_order_line(purchase, item)
- if item.keep_description:
- po_line_data["name"] = item.name
- if line.related_product:
- po_line_data.update({'related_product': line.related_product.id})
- po_line = po_line_obj.create(po_line_data)
- po_line_product_uom_qty = po_line.product_uom._compute_quantity(
- po_line.product_uom_qty, alloc_uom
- )
- wizard_product_uom_qty = wizard_uom._compute_quantity(
- item.product_qty, alloc_uom
- )
- all_qty = min(po_line_product_uom_qty, wizard_product_uom_qty)
- self.create_allocation(po_line, line, all_qty, alloc_uom)
- self._post_process_po_line(item, po_line, new_pr_line)
- res.append(purchase.id)
-
- purchase_requests = self.item_ids.mapped("request_id")
- purchase_requests.button_in_progress()
- return {
- "domain": [("id", "in", res)],
- "name": _("RFQ"),
- "view_mode": "tree,form",
- "res_model": "purchase.order",
- "view_id": False,
- "context": False,
- "type": "ir.actions.act_window",
- }
+ @api.model
+ def _prepare_purchase_order_line(self, po, item):
+ ret = super(PurchaseRequestLineMakePurchaseOrder, self)._prepare_purchase_order_line(po, item)
+ ret['demand_plan_line_id'] = item.line_id.demand_plan_line_id.id
+ return ret
diff --git a/sf_demand_plan/models/sale_order.py b/sf_demand_plan/models/sale_order.py
index fe8966a6..149fadfe 100644
--- a/sf_demand_plan/models/sale_order.py
+++ b/sf_demand_plan/models/sale_order.py
@@ -11,7 +11,7 @@ class ReSaleOrder(models.Model):
groups='mrp.group_mrp_user', store=True)
demand_plan_ids = fields.Many2many(comodel_name="sf.demand.plan",
- string="需求计划", readonly=True)
+ string="需求计划", readonly=True)
demand_plan_count = fields.Integer(
string="需求计划生成计数",
@@ -47,9 +47,9 @@ class ReSaleOrder(models.Model):
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,
+ 'machining_drawings': product.machining_drawings,
'type': '1',
}
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
diff --git a/sf_demand_plan/models/sf_demand_plan.py b/sf_demand_plan/models/sf_demand_plan.py
index bd181937..9b6cc17f 100644
--- a/sf_demand_plan/models/sf_demand_plan.py
+++ b/sf_demand_plan/models/sf_demand_plan.py
@@ -72,6 +72,8 @@ class SfDemandPlan(models.Model):
('4', '低'),
], string='优先级', default='3')
+ overdelivery_allowed = fields.Boolean('可超量发货', default=False)
+
hide_button_release_plan = fields.Boolean(
string='显示下达计划按钮',
compute='_compute_hide_button_release_plan',
@@ -148,9 +150,8 @@ class SfDemandPlan(models.Model):
for line in self:
sum_plan_uom_qty = sum(line.line_ids.mapped('plan_uom_qty'))
pending_qty = line.product_uom_qty - sum_plan_uom_qty
- rounding = line.product_id.uom_id.rounding or 0.01
if float_compare(pending_qty, 0,
- precision_rounding=rounding) == -1:
+ precision_rounding=line.product_id.uom_id.rounding) == -1:
line.pending_qty = 0
else:
line.pending_qty = pending_qty
@@ -170,7 +171,9 @@ class SfDemandPlan(models.Model):
def _compute_state(self):
for line in self:
status_line = line.line_ids.filtered(lambda p: p.status == '60')
- if line.sale_order_id.state == 'cancel':
+ if not line.line_ids:
+ line.state = '10'
+ elif line.sale_order_id.state == 'cancel':
line.state = '50'
line.line_ids.status = '100'
elif len(line.line_ids) == len(status_line):
@@ -187,16 +190,38 @@ class SfDemandPlan(models.Model):
lambda p: p.status in ('50', '60') and p.new_supply_method == 'custom_made')
line.readonly_custom_made_type = bool(production_demand_plan)
+ @api.constrains('line_ids')
+ def check_line_ids(self):
+ for item in self:
+ if not item.line_ids:
+ raise ValidationError('计划不能为空!')
+
+ def write(self, vals):
+ res = super(SfDemandPlan, self).write(vals)
+ if 'line_ids' in vals:
+ for line in self.line_ids:
+ if not line.sale_order_id:
+ line.sale_order_id = self.sale_order_id
+ if not line.sale_order_line_id:
+ line.sale_order_line_id = self.sale_order_line_id
+ return res
+
def name_get(self):
result = []
for plan in self:
- result.append((plan.id, plan.sale_order_id.name))
+ result.append((plan.id, plan.product_id.name))
return result
def button_production_release_plan(self):
line_ids = self.line_ids.filtered(lambda p: p.status == '30')
sum_product_uom_qty = sum(line_ids.mapped('plan_uom_qty'))
- if sum_product_uom_qty > self.product_uom_qty:
+ customer_location_id = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers')
+ if not self.overdelivery_allowed and line_ids.filtered(lambda p: p.location_id.id == customer_location_id):
+ if float_compare(sum_product_uom_qty, self.product_uom_qty,
+ precision_rounding=self.product_id.uom_id.rounding) == 1:
+ raise ValidationError(f"已禁止向合作伙伴/客户超量发货,请更换“补货原因”或将“可超量发货”设置为“是”。")
+ elif float_compare(sum_product_uom_qty, self.product_uom_qty,
+ precision_rounding=self.product_id.uom_id.rounding) == 1:
return {
'name': _('需求计划'),
'type': 'ir.actions.act_window',
diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py
index a0b5bb92..1d4b616a 100644
--- a/sf_demand_plan/models/sf_production_demand_plan.py
+++ b/sf_demand_plan/models/sf_production_demand_plan.py
@@ -28,10 +28,8 @@ class SfProductionDemandPlan(models.Model):
], string='状态', default='30', readonly=True)
demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan",
string="物料需求", readonly=True)
- sale_order_id = fields.Many2one(comodel_name="sale.order", related='demand_plan_id.sale_order_id',
- string="销售订单", readonly=True)
- sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", related='demand_plan_id.sale_order_line_id',
- string="销售订单明细", readonly=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)
sale_order_line_number = fields.Char(string='销售订单行', compute='_compute_sale_order_line_number', store=True)
company_id = fields.Many2one(
related='sale_order_id.company_id',
@@ -58,7 +56,7 @@ class SfProductionDemandPlan(models.Model):
custom_made_type = fields.Selection([
('automation', "自动化产线加工"),
('manual', "人工线下加工"),
- ], string='自制类型', compute='_compute_custom_made_type', store=True)
+ ], string='产线类型', compute='_compute_custom_made_type', store=True)
supply_method = fields.Selection([
('automation', "自动化产线加工"),
@@ -215,9 +213,8 @@ class SfProductionDemandPlan(models.Model):
@api.depends('sale_order_line_id.qty_to_deliver')
def _compute_qty_to_deliver(self):
for line in self:
- rounding = line.product_id.uom_id.rounding or 0.01
if float_compare(line.sale_order_line_id.qty_to_deliver, 0,
- precision_rounding=rounding) == -1:
+ precision_rounding=line.product_id.uom_id.rounding) == -1:
line.qty_to_deliver = 0
else:
line.qty_to_deliver = line.sale_order_line_id.qty_to_deliver
@@ -285,11 +282,15 @@ class SfProductionDemandPlan(models.Model):
def _compute_material_check(self):
for record in self:
if record.mrp_production_ids and record.mrp_production_ids.move_raw_ids:
+ # 获取完成的制造订单
+ done_manufacturing = record.mrp_production_ids.filtered(lambda mo: mo.state == 'done')
+ product_qty = sum(done_manufacturing.mapped('product_qty'))
+ # 需求数量-完成数量
+ product_uom_qty = record.plan_uom_qty - product_qty
total_reserved_availability = sum(
record.mrp_production_ids.mapped('move_raw_ids.reserved_availability'))
- rounding = record.product_id.uom_id.rounding or 0.01
- if float_compare(total_reserved_availability, record.plan_uom_qty,
- precision_rounding=rounding) >= 0:
+ if float_compare(total_reserved_availability, product_uom_qty,
+ precision_rounding=record.product_id.uom_id.rounding) >= 0:
record.material_check = '1' # 已齐套
else:
record.material_check = '0' # 未齐套
@@ -364,11 +365,12 @@ class SfProductionDemandPlan(models.Model):
return action
def button_action_print(self):
+ model_id = self.mapped('model_id')
return {
'res_model': 'sf.demand.plan.print.wizard',
'type': 'ir.actions.act_window',
'name': _("打印"),
- 'domain': [('demand_plan_id', 'in', self.ids)],
+ 'domain': [('model_id', 'in', model_id)],
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
'target': 'new',
}
@@ -581,17 +583,23 @@ class SfProductionDemandPlan(models.Model):
# programming_no = list(set(programming_mrp_production_ids))
# numbers_str = "、".join(programming_no)
# raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
-
- def button_delete(self):
- self.ensure_one()
- if len(self.demand_plan_id.line_ids) == 1:
- raise ValidationError(f"最后一条计划,不能删除!")
- self.unlink()
+ @api.model
+ def unlink(self):
+ for item in self:
+ if item.status not in ('10', '20', '30'):
+ raise ValidationError(u'只能删除状态为【草稿,待确认,需求确认】的需求计划。')
+ else:
+ super(SfProductionDemandPlan, item).unlink()
def button_batch_release_plan(self):
filtered_plan = self.filtered(lambda mo: mo.status == '30')
if not filtered_plan:
raise UserError(_("没有需要下达的计划!"))
+ check_overdelivery_allowed = False
+ if not self.demand_plan_id.overdelivery_allowed:
+ customer_location_id = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers')
+ if self.location_id.id == customer_location_id:
+ check_overdelivery_allowed = True
# 按产品分组并计算总数
product_data = {}
for plan in filtered_plan:
@@ -607,12 +615,15 @@ class SfProductionDemandPlan(models.Model):
# 检查需求超过计划数量的产品
warning_messages = []
for product, data in product_data.items():
- if data['plan_uom_qty'] > data['product_uom_qty']:
+ if float_compare(data['plan_uom_qty'], data['product_uom_qty'],
+ precision_rounding=product.uom_id.rounding) == 1:
warning_messages.append(
_("您正在下达的产品 %s,计划量%s,需求数量为%s,已超过需求数量") %
(product.display_name, data['plan_uom_qty'], data['product_uom_qty'])
)
- if warning_messages:
+ if warning_messages and check_overdelivery_allowed:
+ raise ValidationError(f"已禁止向合作伙伴/客户超量发货,请更换“补货原因”或将“可超量发货”设置为“是”。")
+ elif warning_messages:
warning_message = "\n".join(warning_messages)
return {
'name': _('需求计划'),
@@ -631,7 +642,15 @@ class SfProductionDemandPlan(models.Model):
self.ensure_one()
if not self.new_supply_method:
raise ValidationError(f"供货方式不能为空!")
- if self.plan_uom_qty > self.product_uom_qty:
+ check_overdelivery_allowed = False
+ if not self.demand_plan_id.overdelivery_allowed:
+ customer_location_id = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers')
+ if self.location_id.id == customer_location_id:
+ check_overdelivery_allowed = True
+ if check_overdelivery_allowed:
+ if float_compare(self.plan_uom_qty, self.product_uom_qty,precision_rounding=self.product_id.uom_id.rounding) == 1:
+ raise ValidationError(f"已禁止向合作伙伴/客户超量发货,请更换“补货原因”或将“可超量发货”设置为“是”。")
+ elif float_compare(self.plan_uom_qty, self.product_uom_qty,precision_rounding=self.product_id.uom_id.rounding) == 1:
return {
'name': _('需求计划'),
'type': 'ir.actions.act_window',
@@ -690,9 +709,9 @@ class SfProductionDemandPlan(models.Model):
# 复制成品模板上的属性
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}-{datetime.now().strftime('%Y%m%d%H%M%S')}"
+ 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
diff --git a/sf_demand_plan/views/demand_plan_info.xml b/sf_demand_plan/views/demand_plan_info.xml
index 80344940..414e60d1 100644
--- a/sf_demand_plan/views/demand_plan_info.xml
+++ b/sf_demand_plan/views/demand_plan_info.xml
@@ -36,13 +36,14 @@
+
-
-
+
+
@@ -53,6 +54,7 @@
+
@@ -71,10 +73,39 @@
class="btn-primary"
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ attrs="{'invisible': [('status', 'in', ('50','60','100'))]}"
+ />
+
diff --git a/sf_demand_plan/wizard/sf_demand_plan_print_wizard.py b/sf_demand_plan/wizard/sf_demand_plan_print_wizard.py
index 74c4b12f..8d3a27b4 100644
--- a/sf_demand_plan/wizard/sf_demand_plan_print_wizard.py
+++ b/sf_demand_plan/wizard/sf_demand_plan_print_wizard.py
@@ -9,11 +9,6 @@ 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([
@@ -25,7 +20,7 @@ class SfDemandPlanPrintWizard(models.TransientModel):
('success', '成功'),
('fail', '失败'),
], string='状态', default='not_start')
- machining_drawings = fields.Binary('2D加工图纸', related='product_id.machining_drawings', store=True)
+ machining_drawings = fields.Binary('2D加工图纸')
cnc_worksheet = fields.Binary('程序单')
@@ -44,14 +39,17 @@ class SfDemandPlanPrintWizard(models.TransientModel):
# 执行打印
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}"
+ production_demand_plan_id = self.env['sf.production.demand.plan'].sudo().search(
+ [('model_id', '=', record.model_id)])
+ for production_demand_plan in production_demand_plan_id:
+ t_part, c_part = production_demand_plan.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
+ production_demand_plan.print_count = f"T{t_num}C{c_num}"
success_records.append({
'filename_url': record.filename_url,
})
@@ -78,18 +76,14 @@ class MrpWorkorder(models.Model):
demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search(
[('model_id', '=', record.model_id), ('type', '=', '2')])
if demand_plan_print:
- self.env['sf.demand.plan.print.wizard'].sudo().write(
+ demand_plan_print.write(
{'cnc_worksheet': record.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',
- 'cnc_worksheet': record.cnc_worksheet,
- 'filename_url': record.cnc_worksheet_name
- }
- self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
+ wizard_vals = {
+ 'model_id': record.model_id,
+ 'type': '2',
+ 'cnc_worksheet': record.cnc_worksheet,
+ 'filename_url': record.cnc_worksheet_name
+ }
+ self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
return res
diff --git a/sf_dlm/models/stock_rule_inherit.py b/sf_dlm/models/stock_rule_inherit.py
index 4d715196..0051a42f 100644
--- a/sf_dlm/models/stock_rule_inherit.py
+++ b/sf_dlm/models/stock_rule_inherit.py
@@ -6,50 +6,50 @@ from odoo import models, fields, api, _
class StockRuleInherit(models.Model):
_inherit = 'stock.rule'
- @api.model
- def _run_buy(self, procurements):
- # 判断补货组的采购类型
- procurements_group = {'standard': [], 'outsourcing': []}
- for procurement, rule in procurements:
- is_outsourcing = False
- product = procurement.product_id
- # 获取主 BOM
- bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
-
- if bom:
- # 遍历 BOM 中的组件(即坯料等)
- for line in bom.bom_line_ids:
- raw_material = line.product_id
- # 检查路线
- for route in raw_material.route_ids:
- # print('route.name:', route.name)
- if route.name == '按订单补给外包商':
- is_outsourcing = True
-
- if is_outsourcing:
- procurements_group['outsourcing'].append((procurement, rule))
- else:
- procurements_group['standard'].append((procurement, rule))
-
- for key, value in procurements_group.items():
- super(StockRuleInherit, self)._run_buy(value)
-
- if key == 'outsourcing':
- for procurement, rule in value:
- supplier = procurement.values.get('supplier')
- if supplier:
- domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
- supplier.partner_id)
- logging.info("domain=============: %s", domain)
- po = self.env['purchase.order'].sudo().search([
- ('partner_id', '=', supplier.partner_id.id),
- ('company_id', '=', procurement.company_id.id), # 保证公司一致
- ('origin', 'like', procurement.origin), # 根据来源匹配
- ('state', '=', 'draft') # 状态为草稿
- ], limit=1)
- logging.info("po=: %s", po)
- if po:
- po.write({'purchase_type': 'outsourcing'})
+ # @api.model
+ # def _run_buy(self, procurements):
+ # # 判断补货组的采购类型
+ # procurements_group = {'standard': [], 'outsourcing': []}
+ # for procurement, rule in procurements:
+ # is_outsourcing = False
+ # product = procurement.product_id
+ # # 获取主 BOM
+ # bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
+ #
+ # if bom:
+ # # 遍历 BOM 中的组件(即坯料等)
+ # for line in bom.bom_line_ids:
+ # raw_material = line.product_id
+ # # 检查路线
+ # for route in raw_material.route_ids:
+ # # print('route.name:', route.name)
+ # if route.name == '按订单补给外包商':
+ # is_outsourcing = True
+ #
+ # if is_outsourcing:
+ # procurements_group['outsourcing'].append((procurement, rule))
+ # else:
+ # procurements_group['standard'].append((procurement, rule))
+ #
+ # for key, value in procurements_group.items():
+ # super(StockRuleInherit, self)._run_buy(value)
+ #
+ # if key == 'outsourcing':
+ # for procurement, rule in value:
+ # supplier = procurement.values.get('supplier')
+ # if supplier:
+ # domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
+ # supplier.partner_id)
+ # logging.info("domain=============: %s", domain)
+ # po = self.env['purchase.order'].sudo().search([
+ # ('partner_id', '=', supplier.partner_id.id),
+ # ('company_id', '=', procurement.company_id.id), # 保证公司一致
+ # ('origin', 'like', procurement.origin), # 根据来源匹配
+ # ('state', '=', 'draft') # 状态为草稿
+ # ], limit=1)
+ # logging.info("po=: %s", po)
+ # if po:
+ # po.write({'purchase_type': 'outsourcing'})
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
# super(StockRuleInherit, self)._run_buy(procurements)
diff --git a/sf_warehouse/__manifest__.py b/sf_warehouse/__manifest__.py
index 456d3b96..28f6434e 100644
--- a/sf_warehouse/__manifest__.py
+++ b/sf_warehouse/__manifest__.py
@@ -16,6 +16,7 @@
# 'security/sf_stock_security.xml',
'security/ir.model.access.csv',
'wizard/wizard_view.xml',
+ 'views/product.xml',
'views/view.xml',
'views/shelf_location.xml',
'views/change_stock_move_views.xml',
diff --git a/sf_warehouse/models/__init__.py b/sf_warehouse/models/__init__.py
index 72b9b610..9035056a 100644
--- a/sf_warehouse/models/__init__.py
+++ b/sf_warehouse/models/__init__.py
@@ -1,2 +1,3 @@
from . import model
from . import sync_common
+from . import product
diff --git a/sf_warehouse/models/model.py b/sf_warehouse/models/model.py
index 2cb35264..74daafb4 100644
--- a/sf_warehouse/models/model.py
+++ b/sf_warehouse/models/model.py
@@ -471,7 +471,7 @@ class ShelfLocation(models.Model):
record.display_rfid = record.product_sn_id.rfid if record.product_sn_id else ''
except Exception as e:
record.display_rfid = ''
-
+
@api.depends('product_id')
def _compute_tool(self):
"""计算工具 RFID"""
@@ -594,7 +594,7 @@ class ShelfLocation(models.Model):
_layer_capacity = _cc_code % record.shelf_id.layer_capacity
if _layer_capacity == 0:
_layer_capacity = record.shelf_id.layer_capacity
- else:
+ else:
_layer_capacity = _layer_capacity
_layer = _layer+1
_layer_capacity = f"{_layer_capacity:02d}"
@@ -634,7 +634,7 @@ class SfShelfLocationLot(models.Model):
for item in self:
if item.qty_num > item.qty:
raise ValidationError('变更数量不能比库存数量大!!!')
-
+
class SfStockMoveLine(models.Model):
@@ -899,7 +899,7 @@ class SfStockMoveLine(models.Model):
def _compute_current_location_id(self):
# 批量获取所有相关记录的picking
pickings = self.mapped('picking_id')
-
+
# 构建源picking的移库行与目标位置的映射
origin_location_map = {}
for picking in pickings:
@@ -907,11 +907,11 @@ class SfStockMoveLine(models.Model):
origin_move = picking.move_ids[:1].move_orig_ids[:1]
if not origin_move:
continue
-
+
origin_picking = origin_move.picking_id
if not origin_picking:
continue
-
+
# 为每个picking构建lot_id到location的映射
origin_location_map[picking.id] = {
move_line.lot_id.id: move_line.destination_location_id
@@ -919,17 +919,17 @@ class SfStockMoveLine(models.Model):
lambda ml: ml.destination_location_id and ml.lot_id
)
}
-
+
# 批量更新current_location_id
for record in self:
current_picking = record.picking_id
if not current_picking:
record.current_location_id = False
continue
-
+
# 获取当前picking对应的lot_location映射
lot_dest_map = origin_location_map.get(current_picking.id, {})
-
+
# 查找匹配的lot_id
for move_line in current_picking.move_line_ids:
if move_line.lot_id and move_line.lot_id.id in lot_dest_map:
@@ -1082,6 +1082,10 @@ class SfStockPicking(models.Model):
重写验证方法,当验证时意味着调拨单已经完成,已经移动到了目标货位,所以需要将当前货位的状态改为空闲
"""
res = super(SfStockPicking, self).button_validate()
+ if any(ml.state == 'done' for ml in self.move_line_ids):
+ # 验证产品库存为负库存问题
+ self.move_ids.product_id.verify_product_repertory(self.location_id)
+
for line in self.move_line_ids:
if line:
if line.destination_location_id:
diff --git a/sf_warehouse/models/product.py b/sf_warehouse/models/product.py
new file mode 100644
index 00000000..a0839512
--- /dev/null
+++ b/sf_warehouse/models/product.py
@@ -0,0 +1,29 @@
+from odoo import models, fields
+from odoo.exceptions import ValidationError
+
+
+class SfProductCategory(models.Model):
+ _inherit = 'product.category'
+
+ negative_inventory_allowed = fields.Boolean('可负库存', default=True)
+
+
+class SfProductTemplate(models.Model):
+ _inherit = 'product.product'
+
+ def verify_product_repertory(self, location_id):
+ """
+ 验证产品 负库存
+ """
+ if not location_id:
+ raise ValidationError('当前位置为空!!')
+ elif len(location_id) != 1:
+ raise ValidationError(f'存在多个当前位置{[item.name for item in location_id]}')
+ elif location_id.usage == 'supplier':
+ return True
+ for pp in self:
+ if not pp.categ_id.negative_inventory_allowed:
+ sq = pp.stock_quant_ids.filtered(lambda sq: sq.quantity < 0 and sq.location_id == location_id)
+ if sq:
+ raise ValidationError(
+ f'产品{pp.name}的产品类型设置为不可负库存,当前操作会导致产品{pp.name}在库存{location_id.name}上的库存数量为负!!!')
diff --git a/sf_warehouse/views/product.xml b/sf_warehouse/views/product.xml
new file mode 100644
index 00000000..3844b94c
--- /dev/null
+++ b/sf_warehouse/views/product.xml
@@ -0,0 +1,17 @@
+
+
+
+ product.category.property.form.warehouse
+ product.category
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file