Compare commits
18 Commits
feature/禁止
...
feature/72
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f66f275719 | ||
|
|
07e4cdcaa0 | ||
|
|
0cb473f209 | ||
|
|
ff5925eb06 | ||
|
|
2a7e07b4c0 | ||
|
|
2f21c510bd | ||
|
|
7adaa7e79b | ||
|
|
166d10e7d9 | ||
|
|
36d6a3ed4c | ||
|
|
650ff9b3e2 | ||
|
|
559c6bfb1d | ||
|
|
e83d3f8a1c | ||
|
|
ac0966f3bf | ||
|
|
1f93ba3b42 | ||
|
|
4d2ab82645 | ||
|
|
d02babaf0a | ||
|
|
acb6fd42ca | ||
|
|
c2cb24c60b |
@@ -4,9 +4,10 @@ class StockRule(models.Model):
|
|||||||
_inherit = 'stock.rule'
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
def _run_buy(self, procurements):
|
def _run_buy(self, procurements):
|
||||||
res = super(StockRule, self)._run_buy(procurements)
|
|
||||||
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
|
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
|
||||||
origins = list(set([procurement[0].origin for procurement in procurements]))
|
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:
|
for origin in origins:
|
||||||
pr_ids = self.env["purchase.request"].sudo().search(
|
pr_ids = self.env["purchase.request"].sudo().search(
|
||||||
[('origin', 'like', origin), ('rule_new_add', '=', True), ('state', '=', 'draft')])
|
[('origin', 'like', origin), ('rule_new_add', '=', True), ('state', '=', 'draft')])
|
||||||
|
|||||||
@@ -18,3 +18,12 @@ class MrpBom(models.Model):
|
|||||||
subcontract = self.get_supplier(product.materials_type_id)
|
subcontract = self.get_supplier(product.materials_type_id)
|
||||||
bom_id.subcontractor_id = subcontract.partner_id.id
|
bom_id.subcontractor_id = subcontract.partner_id.id
|
||||||
return bom_id
|
return bom_id
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
"""重写name_get方法,只显示BOM编码"""
|
||||||
|
result = []
|
||||||
|
for record in self:
|
||||||
|
# 只显示BOM编码,如果编码为空则显示名称
|
||||||
|
display_name = record.code or record.name or f'BOM-{record.id}'
|
||||||
|
result.append((record.id, display_name))
|
||||||
|
return result
|
||||||
|
|||||||
@@ -5,38 +5,42 @@ from odoo.tools import float_compare
|
|||||||
class PurchaseOrder(models.Model):
|
class PurchaseOrder(models.Model):
|
||||||
_inherit = 'purchase.order'
|
_inherit = 'purchase.order'
|
||||||
|
|
||||||
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
|
|
||||||
string="需求计划明细", readonly=True)
|
|
||||||
|
|
||||||
def button_confirm(self):
|
def button_confirm(self):
|
||||||
if self.demand_plan_line_id:
|
if self.order_line[0].demand_plan_line_id:
|
||||||
self = self.with_context(
|
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()
|
res = super(PurchaseOrder, self).button_confirm()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.depends('origin', 'demand_plan_line_id')
|
@api.depends('origin')
|
||||||
def _compute_purchase_type(self):
|
def _compute_purchase_type(self):
|
||||||
for purchase in self:
|
for purchase in self:
|
||||||
if purchase.order_line[0].product_id.categ_id.name == '坯料':
|
if purchase.order_line[0].product_id.categ_id.name == '坯料':
|
||||||
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
|
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
|
||||||
purchase.purchase_type = 'outsourcing'
|
purchase.purchase_type = 'outsourcing'
|
||||||
else:
|
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'
|
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'
|
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
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
res = super(PurchaseOrder, self).create(vals)
|
res = super(PurchaseOrderLine, self).create(vals)
|
||||||
if not res.demand_plan_line_id:
|
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'):
|
if self.env.context.get('demand_plan_line_id'):
|
||||||
res.demand_plan_line_id = 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)])
|
mp_ids = self.env['mrp.production'].sudo().search([('name', 'in', origin)])
|
||||||
if mp_ids:
|
if mp_ids:
|
||||||
|
|||||||
@@ -28,83 +28,8 @@ class PurchaseRequestLine(models.Model):
|
|||||||
class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
|
class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
|
||||||
_inherit = "purchase.request.line.make.purchase.order"
|
_inherit = "purchase.request.line.make.purchase.order"
|
||||||
|
|
||||||
def make_purchase_order(self):
|
@api.model
|
||||||
res = []
|
def _prepare_purchase_order_line(self, po, item):
|
||||||
purchase_obj = self.env["purchase.order"]
|
ret = super(PurchaseRequestLineMakePurchaseOrder, self)._prepare_purchase_order_line(po, item)
|
||||||
po_line_obj = self.env["purchase.order.line"]
|
ret['demand_plan_line_id'] = item.line_id.demand_plan_line_id.id
|
||||||
purchase = False
|
return ret
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ReSaleOrder(models.Model):
|
|||||||
groups='mrp.group_mrp_user', store=True)
|
groups='mrp.group_mrp_user', store=True)
|
||||||
|
|
||||||
demand_plan_ids = fields.Many2many(comodel_name="sf.demand.plan",
|
demand_plan_ids = fields.Many2many(comodel_name="sf.demand.plan",
|
||||||
string="需求计划", readonly=True)
|
string="需求计划", readonly=True)
|
||||||
|
|
||||||
demand_plan_count = fields.Integer(
|
demand_plan_count = fields.Integer(
|
||||||
string="需求计划生成计数",
|
string="需求计划生成计数",
|
||||||
@@ -47,9 +47,9 @@ class ReSaleOrder(models.Model):
|
|||||||
if demand_plan.product_id.machining_drawings_name:
|
if demand_plan.product_id.machining_drawings_name:
|
||||||
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
|
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
|
||||||
wizard_vals = {
|
wizard_vals = {
|
||||||
'demand_plan_id': demand_plan.id,
|
|
||||||
'model_id': demand_plan.model_id,
|
'model_id': demand_plan.model_id,
|
||||||
'filename_url': filename_url,
|
'filename_url': filename_url,
|
||||||
|
'machining_drawings': product.machining_drawings,
|
||||||
'type': '1',
|
'type': '1',
|
||||||
}
|
}
|
||||||
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
from odoo.tools import float_compare
|
from odoo.tools import float_compare
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
class SfDemandPlan(models.Model):
|
class SfDemandPlan(models.Model):
|
||||||
@@ -10,8 +11,7 @@ class SfDemandPlan(models.Model):
|
|||||||
_description = 'sf_demand_plan'
|
_description = 'sf_demand_plan'
|
||||||
|
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('10', '需求确认'),
|
('10', '待工艺设计'),
|
||||||
('20', '待工艺设计'),
|
|
||||||
('30', '部分下达'),
|
('30', '部分下达'),
|
||||||
('40', '已下达'),
|
('40', '已下达'),
|
||||||
('50', '取消'),
|
('50', '取消'),
|
||||||
@@ -72,6 +72,8 @@ class SfDemandPlan(models.Model):
|
|||||||
('4', '低'),
|
('4', '低'),
|
||||||
], string='优先级', default='3')
|
], string='优先级', default='3')
|
||||||
|
|
||||||
|
overdelivery_allowed = fields.Boolean('可超量发货', default=False)
|
||||||
|
|
||||||
hide_button_release_plan = fields.Boolean(
|
hide_button_release_plan = fields.Boolean(
|
||||||
string='显示下达计划按钮',
|
string='显示下达计划按钮',
|
||||||
compute='_compute_hide_button_release_plan',
|
compute='_compute_hide_button_release_plan',
|
||||||
@@ -83,6 +85,8 @@ class SfDemandPlan(models.Model):
|
|||||||
compute='_compute_readonly_custom_made_type',
|
compute='_compute_readonly_custom_made_type',
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
demand_plan_number = fields.Char('需求计划号', compute='_compute_demand_plan_number', readonly=True, store=True)
|
||||||
|
origin = fields.Char('来源', related='sale_order_id.name', readonly=True, store=True)
|
||||||
|
|
||||||
@api.depends('product_id.part_number', 'product_id.model_name')
|
@api.depends('product_id.part_number', 'product_id.model_name')
|
||||||
def _compute_part_number(self):
|
def _compute_part_number(self):
|
||||||
@@ -146,9 +150,8 @@ class SfDemandPlan(models.Model):
|
|||||||
for line in self:
|
for line in self:
|
||||||
sum_plan_uom_qty = sum(line.line_ids.mapped('plan_uom_qty'))
|
sum_plan_uom_qty = sum(line.line_ids.mapped('plan_uom_qty'))
|
||||||
pending_qty = line.product_uom_qty - sum_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,
|
if float_compare(pending_qty, 0,
|
||||||
precision_rounding=rounding) == -1:
|
precision_rounding=line.product_id.uom_id.rounding) == -1:
|
||||||
line.pending_qty = 0
|
line.pending_qty = 0
|
||||||
else:
|
else:
|
||||||
line.pending_qty = pending_qty
|
line.pending_qty = pending_qty
|
||||||
@@ -168,7 +171,9 @@ class SfDemandPlan(models.Model):
|
|||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
status_line = line.line_ids.filtered(lambda p: p.status == '60')
|
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.state = '50'
|
||||||
line.line_ids.status = '100'
|
line.line_ids.status = '100'
|
||||||
elif len(line.line_ids) == len(status_line):
|
elif len(line.line_ids) == len(status_line):
|
||||||
@@ -185,16 +190,38 @@ class SfDemandPlan(models.Model):
|
|||||||
lambda p: p.status in ('50', '60') and p.new_supply_method == 'custom_made')
|
lambda p: p.status in ('50', '60') and p.new_supply_method == 'custom_made')
|
||||||
line.readonly_custom_made_type = bool(production_demand_plan)
|
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):
|
def name_get(self):
|
||||||
result = []
|
result = []
|
||||||
for plan in self:
|
for plan in self:
|
||||||
result.append((plan.id, plan.sale_order_id.name))
|
result.append((plan.id, plan.product_id.name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def button_production_release_plan(self):
|
def button_production_release_plan(self):
|
||||||
line_ids = self.line_ids.filtered(lambda p: p.status == '30')
|
line_ids = self.line_ids.filtered(lambda p: p.status == '30')
|
||||||
sum_product_uom_qty = sum(line_ids.mapped('plan_uom_qty'))
|
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 {
|
return {
|
||||||
'name': _('需求计划'),
|
'name': _('需求计划'),
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
@@ -210,3 +237,17 @@ class SfDemandPlan(models.Model):
|
|||||||
else:
|
else:
|
||||||
for demand_plan_line_id in line_ids:
|
for demand_plan_line_id in line_ids:
|
||||||
demand_plan_line_id.action_confirm()
|
demand_plan_line_id.action_confirm()
|
||||||
|
#需求要求取值格式是来源+来源明细行ID,但是来源明细行ID取得就是product_id.name得最后一位,所以这里也直接截取product_id.name
|
||||||
|
@api.depends('product_id.name')
|
||||||
|
def _compute_demand_plan_number(self):
|
||||||
|
for line in self:
|
||||||
|
product_name = line.product_id.name or ''
|
||||||
|
plan_no = None
|
||||||
|
if line.product_id:
|
||||||
|
# 使用正则表达式匹配P-后面的所有字符
|
||||||
|
match = re.search(r'P-(.*)', product_name)
|
||||||
|
if match:
|
||||||
|
plan_no = match.group(1)
|
||||||
|
line.demand_plan_number = plan_no
|
||||||
|
else:
|
||||||
|
line.demand_plan_number = None
|
||||||
@@ -28,10 +28,8 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
], string='状态', default='30', readonly=True)
|
], string='状态', default='30', readonly=True)
|
||||||
demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan",
|
demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan",
|
||||||
string="物料需求", readonly=True)
|
string="物料需求", readonly=True)
|
||||||
sale_order_id = fields.Many2one(comodel_name="sale.order", related='demand_plan_id.sale_order_id',
|
sale_order_id = fields.Many2one(comodel_name="sale.order", string="销售订单", readonly=True)
|
||||||
string="销售订单", readonly=True)
|
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", 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_line_number = fields.Char(string='销售订单行', compute='_compute_sale_order_line_number', store=True)
|
sale_order_line_number = fields.Char(string='销售订单行', compute='_compute_sale_order_line_number', store=True)
|
||||||
company_id = fields.Many2one(
|
company_id = fields.Many2one(
|
||||||
related='sale_order_id.company_id',
|
related='sale_order_id.company_id',
|
||||||
@@ -58,7 +56,7 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
custom_made_type = fields.Selection([
|
custom_made_type = fields.Selection([
|
||||||
('automation', "自动化产线加工"),
|
('automation', "自动化产线加工"),
|
||||||
('manual', "人工线下加工"),
|
('manual', "人工线下加工"),
|
||||||
], string='自制类型', compute='_compute_custom_made_type', store=True)
|
], string='产线类型', compute='_compute_custom_made_type', store=True)
|
||||||
|
|
||||||
supply_method = fields.Selection([
|
supply_method = fields.Selection([
|
||||||
('automation', "自动化产线加工"),
|
('automation', "自动化产线加工"),
|
||||||
@@ -215,9 +213,8 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
@api.depends('sale_order_line_id.qty_to_deliver')
|
@api.depends('sale_order_line_id.qty_to_deliver')
|
||||||
def _compute_qty_to_deliver(self):
|
def _compute_qty_to_deliver(self):
|
||||||
for line in 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,
|
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
|
line.qty_to_deliver = 0
|
||||||
else:
|
else:
|
||||||
line.qty_to_deliver = line.sale_order_line_id.qty_to_deliver
|
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):
|
def _compute_material_check(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.mrp_production_ids and record.mrp_production_ids.move_raw_ids:
|
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(
|
total_reserved_availability = sum(
|
||||||
record.mrp_production_ids.mapped('move_raw_ids.reserved_availability'))
|
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, product_uom_qty,
|
||||||
if float_compare(total_reserved_availability, record.plan_uom_qty,
|
precision_rounding=record.product_id.uom_id.rounding) >= 0:
|
||||||
precision_rounding=rounding) >= 0:
|
|
||||||
record.material_check = '1' # 已齐套
|
record.material_check = '1' # 已齐套
|
||||||
else:
|
else:
|
||||||
record.material_check = '0' # 未齐套
|
record.material_check = '0' # 未齐套
|
||||||
@@ -364,11 +365,12 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
return action
|
return action
|
||||||
|
|
||||||
def button_action_print(self):
|
def button_action_print(self):
|
||||||
|
model_id = self.mapped('model_id')
|
||||||
return {
|
return {
|
||||||
'res_model': 'sf.demand.plan.print.wizard',
|
'res_model': 'sf.demand.plan.print.wizard',
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'name': _("打印"),
|
'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']],
|
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
|
||||||
'target': 'new',
|
'target': 'new',
|
||||||
}
|
}
|
||||||
@@ -581,17 +583,23 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
# programming_no = list(set(programming_mrp_production_ids))
|
# programming_no = list(set(programming_mrp_production_ids))
|
||||||
# numbers_str = "、".join(programming_no)
|
# numbers_str = "、".join(programming_no)
|
||||||
# raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
|
# raise ValidationError(f"编程单号:{numbers_str},请去云平台处理")
|
||||||
|
@api.model
|
||||||
def button_delete(self):
|
def unlink(self):
|
||||||
self.ensure_one()
|
for item in self:
|
||||||
if len(self.demand_plan_id.line_ids) == 1:
|
if item.status not in ('10', '20', '30'):
|
||||||
raise ValidationError(f"最后一条计划,不能删除!")
|
raise ValidationError(u'只能删除状态为【草稿,待确认,需求确认】的需求计划。')
|
||||||
self.unlink()
|
else:
|
||||||
|
super(SfProductionDemandPlan, item).unlink()
|
||||||
|
|
||||||
def button_batch_release_plan(self):
|
def button_batch_release_plan(self):
|
||||||
filtered_plan = self.filtered(lambda mo: mo.status == '30')
|
filtered_plan = self.filtered(lambda mo: mo.status == '30')
|
||||||
if not filtered_plan:
|
if not filtered_plan:
|
||||||
raise UserError(_("没有需要下达的计划!"))
|
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 = {}
|
product_data = {}
|
||||||
for plan in filtered_plan:
|
for plan in filtered_plan:
|
||||||
@@ -607,12 +615,15 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
# 检查需求超过计划数量的产品
|
# 检查需求超过计划数量的产品
|
||||||
warning_messages = []
|
warning_messages = []
|
||||||
for product, data in product_data.items():
|
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(
|
warning_messages.append(
|
||||||
_("您正在下达的产品 %s,计划量%s,需求数量为%s,已超过需求数量") %
|
_("您正在下达的产品 %s,计划量%s,需求数量为%s,已超过需求数量") %
|
||||||
(product.display_name, data['plan_uom_qty'], data['product_uom_qty'])
|
(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)
|
warning_message = "\n".join(warning_messages)
|
||||||
return {
|
return {
|
||||||
'name': _('需求计划'),
|
'name': _('需求计划'),
|
||||||
@@ -631,7 +642,15 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if not self.new_supply_method:
|
if not self.new_supply_method:
|
||||||
raise ValidationError(f"供货方式不能为空!")
|
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 {
|
return {
|
||||||
'name': _('需求计划'),
|
'name': _('需求计划'),
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
@@ -690,9 +709,9 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
|
|
||||||
# 复制成品模板上的属性
|
# 复制成品模板上的属性
|
||||||
self.product_id.product_tmpl_id.copy_template(product_template_id)
|
self.product_id.product_tmpl_id.copy_template(product_template_id)
|
||||||
|
future_time = datetime.now() + timedelta(hours=8)
|
||||||
# 生成BOM单据编码
|
# 生成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
|
order_id = self.sale_order_id
|
||||||
product = self.product_id
|
product = self.product_id
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
<field name="model_machining_precision"/>
|
<field name="model_machining_precision"/>
|
||||||
<field name="inventory_quantity_auto_apply"/>
|
<field name="inventory_quantity_auto_apply"/>
|
||||||
<field name="priority" attrs="{'readonly': [('state', 'in', ('40','50'))]}"/>
|
<field name="priority" attrs="{'readonly': [('state', 'in', ('40','50'))]}"/>
|
||||||
|
<field name="overdelivery_allowed"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
'required': [('new_supply_method', '=', 'custom_made')]}"/>
|
'required': [('new_supply_method', '=', 'custom_made')]}"/>
|
||||||
<field name="route_ids" widget="many2many_tags" optional="hide"/>
|
<field name="route_ids" widget="many2many_tags" optional="hide"/>
|
||||||
<field name="location_id" optional="hide"/>
|
<field name="location_id" optional="hide"/>
|
||||||
<field name="bom_id" optional="hide"/>
|
<field name="bom_id" optional="hide" readonly="1" options="{'no_create': True}"/>
|
||||||
<field name="plan_uom_qty" attrs="{'readonly': [('status', '!=', '30')]}"/>
|
<field name="plan_uom_qty" attrs="{'readonly': [('status', '!=', '30')]}"/>
|
||||||
<field name="blank_arrival_date"/>
|
<field name="blank_arrival_date"/>
|
||||||
<field name="finished_product_arrival_date"/>
|
<field name="finished_product_arrival_date"/>
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ class SfDemandPlanPrintWizard(models.TransientModel):
|
|||||||
_name = 'sf.demand.plan.print.wizard'
|
_name = 'sf.demand.plan.print.wizard'
|
||||||
_description = u'打印向导'
|
_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')
|
model_id = fields.Char('模型ID')
|
||||||
filename_url = fields.Char('文件名/URL')
|
filename_url = fields.Char('文件名/URL')
|
||||||
type = fields.Selection([
|
type = fields.Selection([
|
||||||
@@ -25,7 +20,7 @@ class SfDemandPlanPrintWizard(models.TransientModel):
|
|||||||
('success', '成功'),
|
('success', '成功'),
|
||||||
('fail', '失败'),
|
('fail', '失败'),
|
||||||
], string='状态', default='not_start')
|
], 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('程序单')
|
cnc_worksheet = fields.Binary('程序单')
|
||||||
|
|
||||||
@@ -44,14 +39,17 @@ class SfDemandPlanPrintWizard(models.TransientModel):
|
|||||||
# 执行打印
|
# 执行打印
|
||||||
self.env['jikimo.printing'].sudo().print_pdf(pdf_data)
|
self.env['jikimo.printing'].sudo().print_pdf(pdf_data)
|
||||||
record.status = 'success'
|
record.status = 'success'
|
||||||
t_part, c_part = record.demand_plan_id.print_count.split('C')
|
production_demand_plan_id = self.env['sf.production.demand.plan'].sudo().search(
|
||||||
t_num = int(t_part[1:])
|
[('model_id', '=', record.model_id)])
|
||||||
c_num = int(c_part)
|
for production_demand_plan in production_demand_plan_id:
|
||||||
if record.type == '1':
|
t_part, c_part = production_demand_plan.print_count.split('C')
|
||||||
t_num += 1
|
t_num = int(t_part[1:])
|
||||||
elif record.type == '2':
|
c_num = int(c_part)
|
||||||
c_num += 1
|
if record.type == '1':
|
||||||
record.demand_plan_id.print_count = f"T{t_num}C{c_num}"
|
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({
|
success_records.append({
|
||||||
'filename_url': record.filename_url,
|
'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(
|
demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search(
|
||||||
[('model_id', '=', record.model_id), ('type', '=', '2')])
|
[('model_id', '=', record.model_id), ('type', '=', '2')])
|
||||||
if demand_plan_print:
|
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})
|
{'cnc_worksheet': record.cnc_worksheet, 'filename_url': record.cnc_worksheet_name})
|
||||||
else:
|
else:
|
||||||
demand_plan = self.env['sf.production.demand.plan'].sudo().search(
|
wizard_vals = {
|
||||||
[('product_id', '=', record.product_id.id)])
|
'model_id': record.model_id,
|
||||||
if demand_plan:
|
'type': '2',
|
||||||
wizard_vals = {
|
'cnc_worksheet': record.cnc_worksheet,
|
||||||
'demand_plan_id': demand_plan.id,
|
'filename_url': record.cnc_worksheet_name
|
||||||
'model_id': demand_plan.model_id,
|
}
|
||||||
'type': '2',
|
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
||||||
'cnc_worksheet': record.cnc_worksheet,
|
|
||||||
'filename_url': record.cnc_worksheet_name
|
|
||||||
}
|
|
||||||
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
|
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -6,50 +6,50 @@ from odoo import models, fields, api, _
|
|||||||
class StockRuleInherit(models.Model):
|
class StockRuleInherit(models.Model):
|
||||||
_inherit = 'stock.rule'
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
@api.model
|
# @api.model
|
||||||
def _run_buy(self, procurements):
|
# def _run_buy(self, procurements):
|
||||||
# 判断补货组的采购类型
|
# # 判断补货组的采购类型
|
||||||
procurements_group = {'standard': [], 'outsourcing': []}
|
# procurements_group = {'standard': [], 'outsourcing': []}
|
||||||
for procurement, rule in procurements:
|
# for procurement, rule in procurements:
|
||||||
is_outsourcing = False
|
# is_outsourcing = False
|
||||||
product = procurement.product_id
|
# product = procurement.product_id
|
||||||
# 获取主 BOM
|
# # 获取主 BOM
|
||||||
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
# bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
||||||
|
#
|
||||||
if bom:
|
# if bom:
|
||||||
# 遍历 BOM 中的组件(即坯料等)
|
# # 遍历 BOM 中的组件(即坯料等)
|
||||||
for line in bom.bom_line_ids:
|
# for line in bom.bom_line_ids:
|
||||||
raw_material = line.product_id
|
# raw_material = line.product_id
|
||||||
# 检查路线
|
# # 检查路线
|
||||||
for route in raw_material.route_ids:
|
# for route in raw_material.route_ids:
|
||||||
# print('route.name:', route.name)
|
# # print('route.name:', route.name)
|
||||||
if route.name == '按订单补给外包商':
|
# if route.name == '按订单补给外包商':
|
||||||
is_outsourcing = True
|
# is_outsourcing = True
|
||||||
|
#
|
||||||
if is_outsourcing:
|
# if is_outsourcing:
|
||||||
procurements_group['outsourcing'].append((procurement, rule))
|
# procurements_group['outsourcing'].append((procurement, rule))
|
||||||
else:
|
# else:
|
||||||
procurements_group['standard'].append((procurement, rule))
|
# procurements_group['standard'].append((procurement, rule))
|
||||||
|
#
|
||||||
for key, value in procurements_group.items():
|
# for key, value in procurements_group.items():
|
||||||
super(StockRuleInherit, self)._run_buy(value)
|
# super(StockRuleInherit, self)._run_buy(value)
|
||||||
|
#
|
||||||
if key == 'outsourcing':
|
# if key == 'outsourcing':
|
||||||
for procurement, rule in value:
|
# for procurement, rule in value:
|
||||||
supplier = procurement.values.get('supplier')
|
# supplier = procurement.values.get('supplier')
|
||||||
if supplier:
|
# if supplier:
|
||||||
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
# domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
||||||
supplier.partner_id)
|
# supplier.partner_id)
|
||||||
logging.info("domain=============: %s", domain)
|
# logging.info("domain=============: %s", domain)
|
||||||
po = self.env['purchase.order'].sudo().search([
|
# po = self.env['purchase.order'].sudo().search([
|
||||||
('partner_id', '=', supplier.partner_id.id),
|
# ('partner_id', '=', supplier.partner_id.id),
|
||||||
('company_id', '=', procurement.company_id.id), # 保证公司一致
|
# ('company_id', '=', procurement.company_id.id), # 保证公司一致
|
||||||
('origin', 'like', procurement.origin), # 根据来源匹配
|
# ('origin', 'like', procurement.origin), # 根据来源匹配
|
||||||
('state', '=', 'draft') # 状态为草稿
|
# ('state', '=', 'draft') # 状态为草稿
|
||||||
], limit=1)
|
# ], limit=1)
|
||||||
logging.info("po=: %s", po)
|
# logging.info("po=: %s", po)
|
||||||
if po:
|
# if po:
|
||||||
po.write({'purchase_type': 'outsourcing'})
|
# po.write({'purchase_type': 'outsourcing'})
|
||||||
|
|
||||||
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
||||||
# super(StockRuleInherit, self)._run_buy(procurements)
|
# super(StockRuleInherit, self)._run_buy(procurements)
|
||||||
|
|||||||
Reference in New Issue
Block a user