mirror of
https://github.com/guohuadeng/app-odoo.git
synced 2025-02-23 04:11:36 +02:00
add app_purchase
This commit is contained in:
4
app_purchase_batch_procurement/wizard/__init__.py
Normal file
4
app_purchase_batch_procurement/wizard/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import product_set_supplier_wiz
|
||||
from . import procurement_batch_generator
|
||||
@@ -0,0 +1,129 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo.exceptions import Warning
|
||||
from datetime import datetime, timedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
|
||||
class ProcurementBatchGenerator(models.TransientModel):
|
||||
_name = 'procurement.batch.generator'
|
||||
_description = 'Wizard to create procurements from product tree'
|
||||
|
||||
route_ids = fields.Many2many('stock.location.route', string='Preferred Routes')
|
||||
line_ids = fields.One2many(
|
||||
'procurement.batch.generator.line', 'parent_id',
|
||||
string='Procurement Request Lines')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(ProcurementBatchGenerator, self).default_get(fields)
|
||||
assert isinstance(self.env.context['active_ids'], list),\
|
||||
"context['active_ids'] must be a list"
|
||||
line_ids = []
|
||||
warehouses = self.env['stock.warehouse'].search(
|
||||
[('company_id', '=', self.env.user.company_id.id)])
|
||||
warehouse_id = warehouses and warehouses[0].id or False
|
||||
# product.template是被继承的,所以用同样方法即可
|
||||
for product in self.env['product.product'].browse(
|
||||
self.env.context['active_ids']):
|
||||
# todo: 库存的数量在此使用会导致运算量较大,停用。 如需要再加。未来可以考虑数据全部从前端送过来,不需再查数据库
|
||||
# partner_id = product.seller_ids and product.seller_ids[0].id or False
|
||||
if product.virtual_available < 0:
|
||||
procurement_qty = product.virtual_available * -1
|
||||
else:
|
||||
procurement_qty = 1
|
||||
line_ids.append([0, 0, {
|
||||
'name': product.name,
|
||||
'product_id': product.id,
|
||||
# 'partner_id': partner_id,
|
||||
# 'qty_available': product.qty_available,
|
||||
'virtual_available': product.virtual_available,
|
||||
# 'outgoing_qty': product.outgoing_qty,
|
||||
# 'incoming_qty': product.incoming_qty,
|
||||
'uom_id': product.uom_id.id,
|
||||
# 按预测库存数补货,or 1
|
||||
'procurement_qty': procurement_qty,
|
||||
'warehouse_id': warehouse_id,
|
||||
'date_planned': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
}])
|
||||
try:
|
||||
res.update({
|
||||
'line_ids': line_ids,
|
||||
})
|
||||
except:
|
||||
pass
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def validate(self):
|
||||
self.ensure_one()
|
||||
wiz = self[0]
|
||||
assert wiz.line_ids, 'wizard must have some lines'
|
||||
procs = self.env['procurement.order']
|
||||
for line in wiz.line_ids:
|
||||
if not line.procurement_qty:
|
||||
continue
|
||||
procurement = self.env['procurement.order'].create(
|
||||
line._prepare_procurement_order())
|
||||
procs += procurement
|
||||
if not procs.ids:
|
||||
raise Warning(_('All requested quantities are null.'))
|
||||
# todo: 看是否需要记录工作流
|
||||
# self.pool['procurement.order'].signal_workflow(
|
||||
# self._cr, self._uid, new_po_ids, 'button_confirm')
|
||||
# todo: 看是用 self.pool还是直接run,当前用直接run
|
||||
# self.pool['procurement.order'].run(
|
||||
# self._cr, self._uid, new_po_ids, context=self.env.context)
|
||||
procs.run()
|
||||
action = self.env.ref('procurement.procurement_action').read()[0]
|
||||
action['domain'] = [('id', 'in', procs.ids)]
|
||||
return action
|
||||
|
||||
class ProcurementBatchGeneratorLine(models.TransientModel):
|
||||
_name = 'procurement.batch.generator.line'
|
||||
_description = 'Lines of the wizard to request procurements'
|
||||
|
||||
parent_id = fields.Many2one(
|
||||
'procurement.batch.generator', string='Parent')
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', readonly=True)
|
||||
partner_id = fields.Many2one(
|
||||
'res.partner', string='Supplier', required=False)
|
||||
qty_available = fields.Float(
|
||||
string='Quantity On Hand',
|
||||
digits=dp.get_precision('Product Unit of Measure'), readonly=True)
|
||||
virtual_available = fields.Float(
|
||||
string='Forecast Quantity',
|
||||
digits=dp.get_precision('Product Unit of Measure'), readonly=True)
|
||||
outgoing_qty = fields.Float(
|
||||
string='Incoming Quantity',
|
||||
digits=dp.get_precision('Product Unit of Measure'), readonly=True)
|
||||
incoming_qty = fields.Float(
|
||||
string='Outgoing Quantity',
|
||||
digits=dp.get_precision('Product Unit of Measure'), readonly=True)
|
||||
procurement_qty = fields.Float(
|
||||
string='Requested Quantity',
|
||||
digits=dp.get_precision('Product Unit of Measure'), default=1)
|
||||
uom_id = fields.Many2one(
|
||||
'product.uom', string='Unit of Measure', readonly=True)
|
||||
warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string='Warehouse')
|
||||
date_planned = fields.Datetime(string='Planned Date')
|
||||
|
||||
@api.multi
|
||||
def _prepare_procurement_order(self):
|
||||
self.ensure_one()
|
||||
vals = {
|
||||
'name': 'INT: %s' % (self.env.user.login),
|
||||
'date_planned': self.date_planned,
|
||||
'product_id': self.product_id.id,
|
||||
'product_qty': self.procurement_qty,
|
||||
'product_uom': self.uom_id.id,
|
||||
'location_id': self.warehouse_id.lot_stock_id.id,
|
||||
'company_id': self.warehouse_id.company_id.id,
|
||||
'route_ids': [(6, 0, self.parent_id.route_ids.ids)],
|
||||
}
|
||||
return vals
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="procurement_batch_generator_wiz_form" model="ir.ui.view">
|
||||
<field name="name">procurement.batch.generator.form</field>
|
||||
<field name="model">procurement.batch.generator</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Request Procurements">
|
||||
<group>
|
||||
<field name="route_ids" widget="many2many_tags" groups="stock.group_adv_location" invisible="1"/>
|
||||
</group>
|
||||
<group name="main">
|
||||
<field name="line_ids" context="{'default_parent_id': active_id}" nolabel="1" colspan="2">
|
||||
<tree editable="bottom" create="0" delete="0">
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
<!--<field name="qty_available"/>-->
|
||||
<field name="virtual_available"/>
|
||||
<field name="uom_id" groups="product.group_uom"/>
|
||||
<!--<field name="partner_id" readonly="1"/>-->
|
||||
<field name="procurement_qty"/>
|
||||
<field name="warehouse_id" options="{'no_open':True,'no_create':True}"/>
|
||||
<field name="date_planned" widget="date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button type="object" name="validate"
|
||||
string="Validate" class="oe_highlight"/>
|
||||
<button special="cancel" string="Cancel" class="oe_link"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window id="procurement_batch_generator_tree_action"
|
||||
multi="True"
|
||||
key2="client_action_multi"
|
||||
name="Request Procurements"
|
||||
res_model="procurement.batch.generator"
|
||||
src_model="product.template"
|
||||
view_mode="form"
|
||||
target="new"/>
|
||||
<act_window id="procurement_batch_generator_tree_action2"
|
||||
multi="True"
|
||||
key2="client_action_multi"
|
||||
name="Request Procurements"
|
||||
res_model="procurement.batch.generator"
|
||||
src_model="product.product"
|
||||
view_mode="form"
|
||||
target="new"/>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="product_set_supplier_wiz_form" model="ir.ui.view">
|
||||
<field name="name">product.set.supplier.wiz.form</field>
|
||||
<field name="model">product.set.supplier.wiz</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Set Supplier">
|
||||
<group>
|
||||
<group><field name="name" options="{'no_create': True, 'no_open':True}"/></group>
|
||||
<group><field name="create_uid" readonly="1"/></group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="min_qty"/>
|
||||
<field name="price"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="date_start" string="Validity From"/>
|
||||
<newline/>
|
||||
<field name="date_start" nolabel="1" class="oe_inline"/> 至 <field name="date_end" nolabel="1" class="oe_inline"/>
|
||||
<!--<field name="delay"/>-->
|
||||
</group>
|
||||
</group>
|
||||
<!--<separator string="要设置供应商的补货单(仅对采购类型,且异常的补货单生效)"/>-->
|
||||
<separator string="Procurement list to setup supplier for the product"/>
|
||||
<group>
|
||||
<field name="p_ids" nolabel="1" widget="many2many_tags"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="set_supplier" type="object" string="Set supplier and run Scheduler" class="btn-primary"/>
|
||||
<!--<button name="set_supplier" type="object" string="设定供应商并执行自动采购策略" class="btn-primary"/>-->
|
||||
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||
<field name="view_po" nolabel="1" class="oe_inline" style="margin-left:50px"/>
|
||||
<label for="view_po"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="product_set_supplier_action">
|
||||
<field name="name">Set Supplier</field>
|
||||
<field name="res_model">product.set.supplier.wiz</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="product_set_supplier_wiz_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<act_window id="action_product_set_supplier"
|
||||
multi="True"
|
||||
key2="client_action_multi" name="Set Supplier"
|
||||
res_model="product.set.supplier.wiz" src_model="procurement.order"
|
||||
view_mode="form" target="new" view_type="form"
|
||||
/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from datetime import datetime, timedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo.exceptions import UserError
|
||||
import time
|
||||
|
||||
class ProductSetSupplierWiz(models.TransientModel):
|
||||
_name = 'product.set.supplier.wiz'
|
||||
_description = 'Set supplier for product'
|
||||
|
||||
name = fields.Many2one(
|
||||
'res.partner', 'Vendor',
|
||||
domain=[('supplier', '=', True)], ondelete='cascade', required=True,
|
||||
help="Vendor of this product")
|
||||
min_qty = fields.Float(
|
||||
'Minimal Quantity', default=0.0, required=True,
|
||||
help="The minimal quantity to purchase from this vendor, expressed in the vendor Product Unit of Measure if not any, in the default unit of measure of the product otherwise.")
|
||||
price = fields.Float(
|
||||
'Price', default=0.0, digits=dp.get_precision('Product Price'),
|
||||
required=True, help="The price to purchase a product")
|
||||
company_id = fields.Many2one(
|
||||
'res.company', 'Company',
|
||||
default=lambda self: self.env.user.company_id.id, index=1)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', 'Currency',
|
||||
default=lambda self: self.env.user.company_id.currency_id.id,
|
||||
required=True)
|
||||
date_start = fields.Date('Start Date', help="Start date for this vendor price")
|
||||
date_end = fields.Date('End Date', help="End date for this vendor price")
|
||||
delay = fields.Integer(
|
||||
'Delivery Lead Time', default=1, required=True,
|
||||
help="Lead time in days between the confirmation of the purchase order and the receipt of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning.")
|
||||
|
||||
# 通过过滤得到要设置的补货单
|
||||
p_ids = fields.Many2many('procurement.order', string='Procurement to set', readonly=True)
|
||||
# 设置完后是否直接显示po
|
||||
view_po = fields.Boolean('Show relate RFQ after set supplier', default=True)
|
||||
|
||||
# 默认值
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(ProductSetSupplierWiz, self).default_get(fields)
|
||||
res['create_uid'] = self.env.user.id
|
||||
if not res.get('min_qty'):
|
||||
res['min_qty'] = 1
|
||||
if not res.get('date_start'):
|
||||
res['date_start'] = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
if not res.get('date_end'):
|
||||
res['date_end'] = (datetime.now() + timedelta(days=1)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
procs_ids = self.env.context.get('active_ids')
|
||||
procs = self.env['procurement.order'].browse(procs_ids).filtered(lambda x: x.rule_id.action == 'buy' and x.state == 'exception')
|
||||
p_ids = procs.ids
|
||||
res['p_ids'] = p_ids
|
||||
if len(p_ids) < 1:
|
||||
raise UserError(_("Please select valid procurement orders. Only the 'status=Exception' and 'Buy in Inventory Routes' product can be set!"))
|
||||
# 请选择有效的补货单。只有需要采购的异常补货单才可进行设置!
|
||||
return res
|
||||
|
||||
# 为选定的产品设定供应商,只处理系统没处理的
|
||||
@api.multi
|
||||
def set_supplier(self):
|
||||
self.ensure_one()
|
||||
procs_ids = self.env.context.get('active_ids')
|
||||
procs = self.env['procurement.order'].browse(procs_ids).filtered(lambda x: x.rule_id.action == 'buy' and x.state == 'exception')
|
||||
t_ids = procs.mapped('product_id.product_tmpl_id').ids
|
||||
# 去重复
|
||||
t_ids = list(set(t_ids))
|
||||
for t in t_ids:
|
||||
self.env['product.supplierinfo'].create({
|
||||
'name': self.name.id,
|
||||
'product_tmpl_id': t,
|
||||
'min_qty': self.min_qty,
|
||||
'date_start': self.date_start,
|
||||
'date_end': self.date_end,
|
||||
'delay': self.delay,
|
||||
})
|
||||
time.sleep(1)
|
||||
procs.run()
|
||||
if self.view_po:
|
||||
time.sleep(2)
|
||||
p_names = procs.mapped('purchase_id.name')
|
||||
p_names = list(set(p_names))
|
||||
domain = [["name", "in", p_names]]
|
||||
context = {
|
||||
'search_default_name': p_names[0],
|
||||
}
|
||||
action = self.env.ref('purchase.purchase_rfq').read()[0]
|
||||
if len(p_names) == 1:
|
||||
action.update({
|
||||
'context': context,
|
||||
})
|
||||
elif len(p_names) > 1:
|
||||
action.update({
|
||||
'domain': domain,
|
||||
})
|
||||
return action
|
||||
Reference in New Issue
Block a user